keygen

package module
v2.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 12, 2024 License: MIT Imports: 26 Imported by: 1

README

Keygen Go SDK

godoc reference CI

Package keygen allows Go programs to license and remotely update themselves using the keygen.sh service.

Config

keygen.Account

Account is your Keygen account ID used globally in the SDK. All requests will be made to this account. This should be hard-coded into your app.

keygen.Account = "1fddcec8-8dd3-4d8d-9b16-215cac0f9b52"
keygen.Product

Product is your Keygen product ID used globally in the SDK. All license validations and upgrade requests will be scoped to this product. This should be hard-coded into your app.

keygen.Product = "1f086ec9-a943-46ea-9da4-e62c2180c2f4"
keygen.LicenseKey

LicenseKey is a license key belonging to the end-user (licensee). This will be used for license validations, activations, deactivations and upgrade requests. You will need to prompt the end-user for this value.

You will need to set the license policy's authentication strategy to LICENSE or MIXED.

Setting LicenseKey will take precedence over Token.

keygen.LicenseKey = "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
keygen.Token

Token is an activation token belonging to the licensee. This will be used for license validations, activations, deactivations and upgrade requests. You will need to prompt the end-user for this.

You will need to set the license policy's authentication strategy to TOKEN or MIXED.

keygen.Token = "activ-d66e044ddd7dcc4169ca9492888435d3v3"
keygen.PublicKey

PublicKey is your Keygen account's hex-encoded Ed25519 public key, used for verifying signed license keys and API response signatures. When set, API response signatures will automatically be verified. You may leave it blank to skip verifying response signatures. This should be hard-coded into your app.

keygen.PublicKey = "e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788"
keygen.Logger

Logger is a leveled logger implementation used for printing debug, informational, warning, and error messages. The default log level is LogLevelError. You may provide your own logger which implements LeveledLogger.

keygen.Logger = &CustomLogger{Level: keygen.LogLevelDebug}

Usage

The following top-level functions are available. We recommend starting here.

keygen.Validate(fingerprints ...string)

To validate a license, configure keygen.Account and keygen.Product with your Keygen account details. Then prompt the end-user for their license key or token and set keygen.LicenseKey or keygen.Token, respectively.

The Validate method accepts zero or more fingerprints, which can be used to scope a license validation to a particular device fingerprint and its hardware components. The first fingerprint should be a machine fingerprint, and the rest are optional component fingerprints.

It will return a License object as well as any validation errors that occur. The License object can be used to perform additional actions, such as license.Activate(fingerprint).

license, err := keygen.Validate(fingerprint)
switch {
case err == keygen.ErrLicenseNotActivated:
  panic("license is not activated!")
case err == keygen.ErrLicenseExpired:
  panic("license is expired!")
case err != nil:
  panic("license is invalid!")
}

fmt.Println("License is valid!")
keygen.Upgrade(options keygen.UpgradeOptions)

Check for an upgrade. When an upgrade is available, a Release will be returned which will allow the update to be installed, replacing the currently running binary. When an upgrade is not available, an ErrUpgradeNotAvailable error will be returned indicating the current version is up-to-date.

When a PublicKey is provided, and the release has a Signature, the signature will be cryptographically verified using Ed25519ph before installing. The PublicKey MUST be a personal Ed25519ph public key. It MUST NOT be your Keygen account's public key (method will panic if public keys match).

You can read more about generating a personal keypair and about code signing here.

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
case err != nil:
  fmt.Println("Upgrade check failed!")

  return
}

// Install the upgrade
if err := release.Install(); err != nil {
  panic("upgrade install failed!")
}

fmt.Println("Upgrade complete! Please restart.")

To quickly generate a keypair, use Keygen's CLI:

keygen genkey

Examples

Below are various implementation examples, covering common licensing scenarios and use cases.

License Activation

Validate the license for a particular device fingerprint, and activate when needed. We're using machineid for fingerprinting, which is cross-platform, using the operating system's native GUID.

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 = "A_KEYGEN_LICENSE_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!")
}
Automatic Upgrades

Check for an upgrade and automatically replace the current binary with the newest version.

package main

import "github.com/keygen-sh/keygen-go/v2"

// The current version of the program
const CurrentVersion = "1.0.0"

func main() {
  keygen.PublicKey = "YOUR_KEYGEN_PUBLIC_KEY"
  keygen.Account = "YOUR_KEYGEN_ACCOUNT_ID"
  keygen.Product = "YOUR_KEYGEN_PRODUCT_ID"
  keygen.LicenseKey = "A_KEYGEN_LICENSE_KEY"

  fmt.Printf("Current version: %s\n", CurrentVersion)
  fmt.Println("Checking for upgrades...")

  opts := keygen.UpgradeOptions{CurrentVersion: CurrentVersion, Channel: "stable", PublicKey: "YOUR_COMPANY_PUBLIC_KEY"}

  // Check for upgrade
  release, err := keygen.Upgrade(opts)
  switch {
  case err == keygen.ErrUpgradeNotAvailable:
    fmt.Println("No upgrade available, already at the latest version!")

    return
  case err != nil:
    fmt.Println("Upgrade check failed!")

    return
  }

  fmt.Printf("Upgrade available! Newest version: %s\n", release.Version)
  fmt.Println("Installing upgrade...")

  // Download the upgrade and install it
  err = release.Install()
  if err != nil {
    panic("upgrade install failed!")
  }

  fmt.Printf("Upgrade complete! Installed version: %s\n", release.Version)
  fmt.Println("Restart to finish installation...")
}
Monitor Machine Heartbeats

Monitor a machine's heartbeat, and automatically deactivate machines in case of a crash or an unresponsive node. We recommend using a random UUID fingerprint for activating nodes in cloud-based scenarios, since nodes may share underlying hardware.

package main

import (
  "github.com/google/uuid"
  "github.com/keygen-sh/keygen-go/v2"
)

func main() {
  keygen.Account = "YOUR_KEYGEN_ACCOUNT_ID"
  keygen.Product = "YOUR_KEYGEN_PRODUCT_ID"
  keygen.LicenseKey = "A_KEYGEN_LICENSE_KEY"

  // The current device's fingerprint (could be e.g. MAC, mobo ID, GUID, etc.)
  fingerprint := uuid.New().String()

  // Keep our example process alive
  done := make(chan bool, 1)

  // 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)
    if err != nil {
      fmt.Println("machine activation failed!")

      panic(err)
    }

    // Handle SIGINT and gracefully deactivate the machine
    sigs := make(chan os.Signal, 1)

    signal.Notify(sigs, os.Interrupt)

    go func() {
      for sig := range sigs {
        fmt.Printf("Caught %v, deactivating machine and gracefully exiting...\n", sig)

        if err := machine.Deactivate(); err != nil {
          panic(err)
        }

        fmt.Println("Machine was deactivated!")
        fmt.Println("Exiting...")

        done <- true
      }
    }()

    // Start a heartbeat monitor for the current machine
    if err := machine.Monitor(); err != nil {
      fmt.Println("Machine heartbeat monitor failed to start!")

      panic(err)
    }

    fmt.Println("Machine is activated and monitored!")
  case err != nil:
    fmt.Println("License is invalid!")

    panic(err)
  }

  fmt.Println("License is valid!")

  <-done
}
Offline License Files

Cryptographically verify and decrypt an encrypted license file. This is useful for checking if a license file is genuine in offline or air-gapped environments. Returns the license file's dataset and any errors that occurred during verification and decryption, e.g. ErrLicenseFileNotGenuine.

When decrypting a license file, you MUST provide the license's key as the decryption key.

When initializing a LicenseFile, Certificate is required.

Requires that keygen.PublicKey is set.

package main

import "github.com/keygen-sh/keygen-go/v2"

func main() {
  keygen.PublicKey = "YOUR_KEYGEN_PUBLIC_KEY"

  // Read the license file
  cert, err := ioutil.ReadFile("/etc/example/license.lic")
  if err != nil {
    panic("license file is missing")
  }

  // Verify the license file's signature
  lic := &keygen.LicenseFile{Certificate: string(cert)}
  err = lic.Verify()
  switch {
  case err == keygen.ErrLicenseFileNotGenuine:
    panic("license file is not genuine!")
  case err != nil:
    panic(err)
  }

  // Use the license key to decrypt the license file
  dataset, err := lic.Decrypt("A_KEYGEN_LICENSE_KEY")
  switch {
  case err == keygen.ErrSystemClockUnsynced:
    panic("system clock tampering detected!")
  case err == keygen.ErrLicenseFileExpired:
    panic("license file is expired!")
  case err != nil:
    panic(err)
  }

  fmt.Println("License file is genuine!")
  fmt.Printf("Decrypted dataset: %v\n", dataset)
}
Offline License Keys

Cryptographically verify and decode a signed license key. This is useful for checking if a license key is genuine in offline or air-gapped environments. Returns the key's decoded dataset and any errors that occurred during cryptographic verification, e.g. ErrLicenseKeyNotGenuine.

When initializing a License, Scheme and Key are required.

Requires that keygen.PublicKey is set.

package main

import "github.com/keygen-sh/keygen-go/v2"

func main() {
  keygen.PublicKey = "YOUR_KEYGEN_PUBLIC_KEY"

  // Verify the license key's signature and decode embedded dataset
  license := &keygen.License{Scheme: keygen.SchemeCodeEd25519, Key: "A_SIGNED_KEYGEN_LICENSE_KEY"}
  dataset, err := license.Verify()
  switch {
  case err == keygen.ErrLicenseKeyNotGenuine:
    panic("license key is not genuine!")
  case err != nil:
    panic(err)
  }

  fmt.Println("License is genuine!")
  fmt.Printf("Decoded dataset: %s\n", dataset)
}
Verify Webhooks

When listening for webhook events from Keygen, you can verify requests came from Keygen's servers by using keygen.VerifyWebhook. This protects your webhook endpoint from event forgery and replay attacks.

Requires that keygen.PublicKey is set.

package main

import (
  "log"
  "net/http"

  "github.com/keygen-sh/keygen-go/v2"
)

func main() {
  keygen.PublicKey = "YOUR_KEYGEN_PUBLIC_KEY"

  http.HandleFunc("/webhooks", func(w http.ResponseWriter, r *http.Request) {
    if err := keygen.VerifyWebhook(r); err != nil {
      w.WriteHeader(http.StatusBadRequest)

      return
    }

    w.WriteHeader(http.StatusNoContent)
  })

  log.Fatal(http.ListenAndServe(":8081", nil))
}

Error Handling

Our SDK tries to return meaningful errors which can be handled in your integration. Below are a handful of error recipes that can be used for the more common errors.

Invalid License Key

When authenticating with a license key, you may receive a LicenseKeyError when the license key does not exist. You can handle this accordingly.

package main

import "github.com/keygen-sh/keygen-go/v2"

func getLicense() (*keygen.License, error) {
  keygen.LicenseKey = promptForLicenseKey()

  license, err := keygen.Validate()
  if err != nil {
    if _, ok := err.(*keygen.LicenseKeyError); ok {
      fmt.Println("License key does not exist!")

      return getLicense()
    }

    return nil, err
  }

  return license, nil
}

func main() {
  keygen.Account = "..."
  keygen.Product = "..."

  license, err := getLicense()
  if err != nil {
    panic(err)
  }

  fmt.Printf("License: %v\n", license)
}
Invalid License Token

When authenticating with a license token, you may receive a LicenseTokenError when the license token does not exist or has expired. You can handle this accordingly.

package main

import "github.com/keygen-sh/keygen-go/v2"

func getLicense() (*keygen.License, error) {
  keygen.Token = promptForLicenseToken()

  license, err := keygen.Validate()
  if err != nil {
    if _, ok := err.(*keygen.LicenseTokenError); ok {
      fmt.Println("License token does not exist!")

      return getLicense()
    }

    return nil, err
  }

  return license, nil
}

func main() {
  keygen.Account = "..."
  keygen.Product = "..."

  license, err := getLicense()
  if err != nil {
    panic(err)
  }

  fmt.Printf("License: %v\n", license)
}
Rate Limiting

When your integration makes too many requests too quickly, the IP address may be rate limited. You can handle this via the RateLimitError error. For example, you could use this error to determine how long to wait before retrying a request.

package main

import "github.com/keygen-sh/keygen-go/v2"

func validate() (*keygen.License, error) {
  license, err := keygen.Validate()
  if err != nil {
    if e, ok := err.(*keygen.RateLimitError); ok {
      // Sleep until our rate limit window is passed
      time.Sleep(time.Duration(e.RetryAfter) * time.Second)

      // Retry validate
      return validate()
    }

    return nil, err
  }

  return license, nil
}

func main() {
  keygen.Account = "YOUR_KEYGEN_ACCOUNT_ID"
  keygen.Product = "YOUR_KEYGEN_PRODUCT_ID"
  keygen.LicenseKey = "A_KEYGEN_LICENSE_KEY"

  license, err := validate()
  if err != nil {
    panic(err)
  }

  fmt.Printf("License: %v\n", license)
}

You may want to add a limit to the number of retry attempts.

Automatic retries

When your integration has less-than-stellar network connectivity, or you simply want to ensure that failed requests are retried, you can utilize a package such as retryablehttp to implement automatic retries.

package main

import (
  "github.com/hashicorp/go-retryablehttp"
  "github.com/keygen-sh/keygen-go/v2"
)

func main() {
  c := retryablehttp.NewClient()

  // Configure with a jitter backoff and max attempts
  c.Backoff = retryablehttp.LinearJitterBackoff
  c.RetryMax = 5

  keygen.HTTPClient = c.StandardClient()
  keygen.Account = "YOUR_KEYGEN_ACCOUNT_ID"
  keygen.Product = "YOUR_KEYGEN_PRODUCT_ID"
  keygen.LicenseKey = "A_KEYGEN_LICENSE_KEY"

  // Use SDK as you would normally
  keygen.Validate()
}

Testing

When implementing a testing strategy for your licensing integration, we recommend that you fully mock our APIs. This is especially important for CI/CD environments, to prevent unneeded load on our servers. Mocking our APIs will also allow you to more easily stay within your account's daily request limits.

To do so in Go, you can utilize gock or httptest.

package main

import (
  "testing"

  "github.com/keygen-sh/keygen-go/v2"
  "gopkg.in/h2non/gock.v1"
)

func init() {
  keygen.PublicKey = "e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788"
  keygen.Account = "1fddcec8-8dd3-4d8d-9b16-215cac0f9b52"
  keygen.Product = "1f086ec9-a943-46ea-9da4-e62c2180c2f4"
  keygen.LicenseKey = "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
}

func TestExample(t *testing.T) {
  defer gock.Off()

  // Intercept Keygen's HTTP client
  gock.InterceptClient(keygen.HTTPClient)
  defer gock.RestoreClient(keygen.HTTPClient)

  // Mock endpoints
  gock.New("https://api.keygen.sh").
    Get(`/v1/accounts/([^\/]+)/me`).
    Reply(200).
    SetHeader("Keygen-Signature", `keyid="1fddcec8-8dd3-4d8d-9b16-215cac0f9b52", algorithm="ed25519", signature="IiyYX1ah2HFzbcCx+3sv+KJpOppFdMRuZ7NWlnwZMKAf5khj9c4TO4z6fr62BqNXlyROOTxZinX8UpXHJHVyAw==", headers="(request-target) host date digest"`).
    SetHeader("Digest", "sha-256=d4uZ26hjiUNqopuSkYcYwg2aBuNtr4D1/9iDhlvf0H8=").
    SetHeader("Date", "Wed, 15 Jun 2022 18:52:14 GMT").
    BodyString(`{"data":{"id":"218810ed-2ac8-4c26-a725-a6da67500561","type":"licenses","attributes":{"name":"Demo License","key":"C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3","expiry":null,"status":"ACTIVE","uses":0,"suspended":false,"scheme":null,"encrypted":false,"strict":false,"floating":false,"concurrent":false,"protected":true,"maxMachines":1,"maxProcesses":null,"maxCores":null,"maxUses":null,"requireHeartbeat":false,"requireCheckIn":false,"lastValidated":"2022-06-15T18:52:12.068Z","lastCheckIn":null,"nextCheckIn":null,"metadata":{"email":"user@example.com"},"created":"2020-09-14T21:18:08.990Z","updated":"2022-06-15T18:52:12.073Z"},"relationships":{"account":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52"},"data":{"type":"accounts","id":"1fddcec8-8dd3-4d8d-9b16-215cac0f9b52"}},"product":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/product"},"data":{"type":"products","id":"ef6e0993-70d6-42c4-a0e8-846cb2e3fa54"}},"policy":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/policy"},"data":{"type":"policies","id":"629307fb-331d-430b-978a-44d45d9de133"}},"group":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/group"},"data":null},"user":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/user"},"data":null},"machines":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/machines"},"meta":{"cores":0,"count":1}},"tokens":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/tokens"}},"entitlements":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/entitlements"}}},"links":{"self":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561"}}}`)

  gock.New("https://api.keygen.sh").
    Post(`/v1/accounts/([^\/]+)/licenses/([^\/]+)/actions/validate`).
    Reply(200).
    SetHeader("Keygen-Signature", `keyid="1fddcec8-8dd3-4d8d-9b16-215cac0f9b52", algorithm="ed25519", signature="18+5Q4749BKuUz9/f35UrdP5g3Pyt32pPN3J8e5BqSlRbqiXnz0HwtqbP5sbvGkq1yixelwgV6bcJ0WUtpDSBw==", headers="(request-target) host date digest"`).
    SetHeader("Digest", "sha-256=c1y1CVVLG0mvt0MP1SJy/bOiNjCytxMOuHUhlCXXVVk=").
    SetHeader("Date", "Thu, 09 Feb 2023 21:20:13 GMT").
    BodyString(`{"data":{"id":"218810ed-2ac8-4c26-a725-a6da67500561","type":"licenses","attributes":{"name":"Demo License","key":"C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3","expiry":null,"status":"ACTIVE","uses":0,"suspended":false,"scheme":null,"encrypted":false,"strict":false,"floating":false,"protected":true,"maxMachines":1,"maxProcesses":null,"maxCores":null,"maxUses":null,"requireHeartbeat":false,"requireCheckIn":false,"lastValidated":"2023-02-09T21:20:13.679Z","lastCheckIn":null,"nextCheckIn":null,"lastCheckOut":null,"metadata":{"email":"user@example.com"},"created":"2020-09-14T21:18:08.990Z","updated":"2023-02-09T21:20:13.691Z"},"relationships":{"account":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52"},"data":{"type":"accounts","id":"1fddcec8-8dd3-4d8d-9b16-215cac0f9b52"}},"product":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/product"},"data":{"type":"products","id":"ef6e0993-70d6-42c4-a0e8-846cb2e3fa54"}},"policy":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/policy"},"data":{"type":"policies","id":"629307fb-331d-430b-978a-44d45d9de133"}},"group":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/group"},"data":null},"user":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/user"},"data":null},"machines":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/machines"},"meta":{"cores":0,"count":1}},"tokens":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/tokens"}},"entitlements":{"links":{"related":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561/entitlements"}}},"links":{"self":"/v1/accounts/1fddcec8-8dd3-4d8d-9b16-215cac0f9b52/licenses/218810ed-2ac8-4c26-a725-a6da67500561"}},"meta":{"ts":"2023-02-09T21:20:13.696Z","valid":true,"detail":"is valid","code":"VALID"}}`)

  // Allow old response signatures
  keygen.MaxClockDrift = -1

  // Use SDK as you would normally
  _, err := keygen.Validate()
  if err != nil {
    t.Fatalf("Should not fail mock validation: err=%v", err)
  }
}

Documentation

Overview

Package keygen allows programs to license and remotely update themselves using the keygen.sh service.

Index

Constants

View Source
const (
	// The current version of the SDK.
	SDKVersion = "2.9.0"
)

Variables

View Source
var (
	ErrReleaseLocationMissing       = errors.New("release has no download URL")
	ErrUpgradeNotAvailable          = errors.New("no upgrades available (already up-to-date)")
	ErrResponseSignatureMissing     = errors.New("response signature is missing")
	ErrResponseSignatureInvalid     = errors.New("response signature is invalid")
	ErrResponseDigestMissing        = errors.New("response digest is missing")
	ErrResponseDigestInvalid        = errors.New("response digest is invalid")
	ErrResponseDateMissing          = errors.New("response date is missing")
	ErrResponseDateInvalid          = errors.New("response date is invalid")
	ErrResponseDateTooOld           = errors.New("response date is too old")
	ErrRequestSignatureMissing      = errors.New("request signature is missing")
	ErrRequestSignatureInvalid      = errors.New("request signature is invalid")
	ErrRequestDigestMissing         = errors.New("request digest is missing")
	ErrRequestDigestInvalid         = errors.New("request digest is invalid")
	ErrRequestDateMissing           = errors.New("request date is missing")
	ErrRequestDateInvalid           = errors.New("request date is invalid")
	ErrRequestDateTooOld            = errors.New("request date is too old")
	ErrPublicKeyMissing             = errors.New("public key is missing")
	ErrPublicKeyInvalid             = errors.New("public key is invalid")
	ErrValidationFingerprintMissing = errors.New("validation fingerprint scope is missing")
	ErrValidationComponentsMissing  = errors.New("validation components scope is missing")
	ErrValidationProductMissing     = errors.New("validation product scope is missing")
	ErrHeartbeatPingFailed          = errors.New("heartbeat ping failed")
	ErrHeartbeatRequired            = errors.New("heartbeat is required")
	ErrHeartbeatDead                = errors.New("heartbeat is dead")
	ErrMachineAlreadyActivated      = errors.New("machine is already activated")
	ErrMachineLimitExceeded         = errors.New("machine limit has been exceeded")
	ErrMachineNotFound              = errors.New("machine no longer exists")
	ErrProcessNotFound              = errors.New("process no longer exists")
	ErrMachineFileNotSupported      = errors.New("machine file is not supported")
	ErrMachineFileNotEncrypted      = errors.New("machine file is not encrypted")
	ErrMachineFileNotGenuine        = errors.New("machine file is not genuine")
	ErrMachineFileExpired           = errors.New("machine file is expired")
	ErrComponentNotActivated        = errors.New("component is not activated")
	ErrComponentAlreadyActivated    = errors.New("component is already activated")
	ErrComponentConflict            = errors.New("component is duplicated")
	ErrProcessLimitExceeded         = errors.New("process limit has been exceeded")
	ErrLicenseSchemeNotSupported    = errors.New("license scheme is not supported")
	ErrLicenseSchemeMissing         = errors.New("license scheme is missing")
	ErrLicenseKeyMissing            = errors.New("license key is missing")
	ErrLicenseKeyNotGenuine         = errors.New("license key is not genuine")
	ErrLicenseNotActivated          = errors.New("license is not activated")
	ErrLicenseExpired               = errors.New("license is expired")
	ErrLicenseSuspended             = errors.New("license is suspended")
	ErrLicenseTooManyMachines       = errors.New("license has too many machines")
	ErrLicenseTooManyCores          = errors.New("license has too many cores")
	ErrLicenseTooManyProcesses      = errors.New("license has too many processes")
	ErrLicenseNotSigned             = errors.New("license is not signed")
	ErrLicenseInvalid               = errors.New("license is invalid")
	ErrLicenseFileNotSupported      = errors.New("license file is not supported")
	ErrLicenseFileNotEncrypted      = errors.New("license file is not encrypted")
	ErrLicenseFileNotGenuine        = errors.New("license file is not genuine")
	ErrLicenseFileExpired           = errors.New("license file is expired")
	ErrLicenseFileSecretMissing     = errors.New("license file secret is missing")
	ErrSystemClockUnsynced          = errors.New("system clock is out of sync")
)

General errors

View Source
var (
	// APIURL is the URL of the API service backend.
	APIURL = "https://api.keygen.sh"

	// APIVersion is the currently supported API version.
	APIVersion = "1.5"

	// APIPrefix is the major version prefix included in all API URLs.
	APIPrefix = "v1"

	// Account is the Keygen account identifier used globally in the SDK.
	Account string

	// Product is the Keygen product identifier used globally in the SDK.
	Product string

	// Package is the Keygen package identifier used globally in the SDK.
	Package string

	// Environment is the Keygen environment identifier used globally in the SDK.
	Environment string

	// LicenseKey is the end-user's license key used in the SDK.
	LicenseKey string

	// Token is the end-user's API token used in the SDK.
	Token string

	// PublicKey is the Keygen public key used for verifying license keys
	// and API response signatures.
	PublicKey string

	// UserAgent defines the user-agent string sent to the API backend,
	// uniquely identifying an integration.
	UserAgent string

	// Logger is a leveled logger implementation used for printing debug,
	// informational, warning, and error messages.
	Logger LeveledLogger = &logger{Level: LogLevelError}

	// Program is the name of the current program, used when installing
	// upgrades. Defaults to the current program name.
	Program = filepath.Base(os.Args[0])

	// MaxClockDrift is the maximum allowable difference between the
	// server time Keygen's API sent a request or response and the
	// current system time, to prevent clock-tampering and replay
	// attacks. Set to -1 to disable.
	MaxClockDrift = time.Duration(5) * time.Minute

	// HTTPClient is the internal HTTP client used by the SDK for API
	// requests. Set this to a custom HTTP client, to implement e.g.
	// automatic retries, rate limiting checks, or for tests.
	HTTPClient = cleanhttp.DefaultPooledClient()
)
View Source
var (
	// Ext is the release artifact filename extension used when installing
	// upgrades. By default, binaries do not have an extension.
	Ext = ""
)

Functions

func VerifyWebhook

func VerifyWebhook(request *http.Request) error

VerifyWebhook verifies the signature of a webhook request sent from Keygen. The webhook event should be considered invalid if an error is returned.

Example:

func main() {
	http.HandleFunc("/webhooks", func(w http.ResponseWriter, r *http.Request) {
		if err := keygen.VerifyWebhook(r); err != nil {
			w.WriteHeader(http.StatusBadRequest)

			return
		}

		w.WriteHeader(http.StatusNoContent)
	})

	http.ListenAndServe(":8081", nil)
}

Types

type Artifact

type Artifact struct {
	ID        string    `json:"-"`
	Type      string    `json:"-"`
	Filename  string    `json:"filename"`
	Filetype  string    `json:"filetype"`
	Filesize  int64     `json:"filesize"`
	Platform  string    `json:"platform"`
	Arch      string    `json:"arch"`
	Signature string    `json:"signature"`
	Checksum  string    `json:"checksum"`
	Created   time.Time `json:"created"`
	Updated   time.Time `json:"updated"`
	ReleaseId string    `json:"-"`
	URL       string    `json:"-"`
}

Artifact represents a Keygen artifact object.

func (*Artifact) SetData

func (a *Artifact) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

func (*Artifact) SetID

func (a *Artifact) SetID(id string) error

SetID implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*Artifact) SetRelationships

func (a *Artifact) SetRelationships(relationships map[string]interface{}) error

SetRelationships implements the jsonapi.UnmarshalRelationship interface.

func (*Artifact) SetType

func (a *Artifact) SetType(t string) error

SetType implements the jsonapi.UnmarshalResourceIdentifier interface.

type CheckoutOption added in v2.7.0

type CheckoutOption func(*CheckoutOptions) error

func CheckoutInclude added in v2.7.0

func CheckoutInclude(includes ...string) CheckoutOption

func CheckoutTTL added in v2.7.0

func CheckoutTTL(ttl time.Duration) CheckoutOption

type CheckoutOptions added in v2.7.0

type CheckoutOptions struct {
	Encrypt bool   `url:"encrypt"`
	Include string `url:"include,omitempty"`
	TTL     int    `url:"ttl,omitempty"`
}

type Client

type Client struct {
	HTTPClient *http.Client
	ClientOptions
	// contains filtered or unexported fields
}

Client represents the internal HTTP client and config used for API requests.

func NewClient

func NewClient() *Client

NewClient creates a new Client with default settings.

func NewClientWithOptions

func NewClientWithOptions(options *ClientOptions) *Client

NewClientWithOptions creates a new client with custom settings.

func (*Client) Delete

func (c *Client) Delete(path string, params interface{}, model interface{}) (*Response, error)

Delete is a convenience helper for performing DELETE requests.

func (*Client) Get

func (c *Client) Get(path string, params interface{}, model interface{}) (*Response, error)

Get is a convenience helper for performing GET requests.

func (*Client) Patch

func (c *Client) Patch(path string, params interface{}, model interface{}) (*Response, error)

Patch is a convenience helper for performing PATCH requests.

func (*Client) Post

func (c *Client) Post(path string, params interface{}, model interface{}) (*Response, error)

Post is a convenience helper for performing POST requests.

func (*Client) Put

func (c *Client) Put(path string, params interface{}, model interface{}) (*Response, error)

Put is a convenience helper for performing PUT requests.

type ClientOptions

type ClientOptions struct {
	Account     string
	Environment string
	LicenseKey  string
	Token       string
	PublicKey   string
	UserAgent   string
	APIVersion  string
	APIPrefix   string
	APIURL      string
}

ClientOptions stores config options used in API requests.

type Component added in v2.8.0

type Component struct {
	ID          string                 `json:"-"`
	Type        string                 `json:"-"`
	Fingerprint string                 `json:"fingerprint"`
	Name        string                 `json:"name"`
	Created     time.Time              `json:"created"`
	Updated     time.Time              `json:"updated"`
	Metadata    map[string]interface{} `json:"metadata"`
	MachineID   string                 `json:"-"`
}

Component represents a Keygen component object.

func (Component) GetData added in v2.8.0

func (c Component) GetData() interface{}

GetData implements the jsonapi.MarshalData interface.

func (Component) GetID added in v2.8.0

func (c Component) GetID() string

GetID implements the jsonapi.MarshalResourceIdentifier interface.

func (Component) GetType added in v2.8.0

func (c Component) GetType() string

GetType implements the jsonapi.MarshalResourceIdentifier interface.

func (*Component) SetData added in v2.8.0

func (c *Component) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

func (*Component) SetID added in v2.8.0

func (c *Component) SetID(id string) error

SetID implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*Component) SetType added in v2.8.0

func (c *Component) SetType(t string) error

SetType implements the jsonapi.UnmarshalResourceIdentifier interface.

func (Component) UseExperimentalEmbeddedRelationshipData added in v2.8.0

func (c Component) UseExperimentalEmbeddedRelationshipData() bool

type Components added in v2.8.0

type Components []Component

Components represents an array of component objects.

func (*Components) SetData added in v2.8.0

func (c *Components) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

type Entitlement

type Entitlement struct {
	ID       string                 `json:"-"`
	Type     string                 `json:"-"`
	Code     EntitlementCode        `json:"code"`
	Created  time.Time              `json:"created"`
	Updated  time.Time              `json:"updated"`
	Metadata map[string]interface{} `json:"metadata"`
}

Entitlement represents a Keygen entitlement object.

func (*Entitlement) SetData

func (e *Entitlement) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

func (*Entitlement) SetID

func (e *Entitlement) SetID(id string) error

SetID implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*Entitlement) SetType

func (e *Entitlement) SetType(t string) error

SetType implements the jsonapi.UnmarshalResourceIdentifier interface.

type EntitlementCode

type EntitlementCode string

type Entitlements

type Entitlements []Entitlement

Entitlements represents an array of entitlement objects.

func (*Entitlements) SetData

func (e *Entitlements) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

type EnvironmentError added in v2.5.0

type EnvironmentError struct{ Err *Error }

EnvironmentError represents an API environment error due to an invalid environment.

func (*EnvironmentError) Error added in v2.5.0

func (e *EnvironmentError) Error() string

func (*EnvironmentError) Unwrap added in v2.5.0

func (e *EnvironmentError) Unwrap() error

type Error

type Error struct {
	Response *Response
	Title    string
	Detail   string
	Code     string
	Source   string
}

Error represents an API error response.

func (*Error) Error

func (e *Error) Error() string

type ErrorCode

type ErrorCode string

ErrorCode defines various error codes that are handled explicitly.

const (
	ErrorCodeEnvironmentInvalid           ErrorCode = "ENVIRONMENT_INVALID"
	ErrorCodeEnvironmentNotSupported      ErrorCode = "ENVIRONMENT_NOT_SUPPORTED"
	ErrorCodeTokenInvalid                 ErrorCode = "TOKEN_INVALID"
	ErrorCodeLicenseInvalid               ErrorCode = "LICENSE_INVALID"
	ErrorCodeLicenseExpired               ErrorCode = "LICENSE_EXPIRED"
	ErrorCodeLicenseSuspended             ErrorCode = "LICENSE_SUSPENDED"
	ErrorCodeFingerprintTaken             ErrorCode = "FINGERPRINT_TAKEN"
	ErrorCodeMachineLimitExceeded         ErrorCode = "MACHINE_LIMIT_EXCEEDED"
	ErrorCodeProcessLimitExceeded         ErrorCode = "MACHINE_PROCESS_LIMIT_EXCEEDED"
	ErrorCodeComponentFingerprintConflict ErrorCode = "COMPONENTS_FINGERPRINT_CONFLICT"
	ErrorCodeComponentFingerprintTaken    ErrorCode = "COMPONENTS_FINGERPRINT_TAKEN"
	ErrorCodeMachineHeartbeatDead         ErrorCode = "MACHINE_HEARTBEAT_DEAD"
	ErrorCodeProcessHeartbeatDead         ErrorCode = "PROCESS_HEARTBEAT_DEAD"
	ErrorCodeNotFound                     ErrorCode = "NOT_FOUND"
)

type HeartbeatStatusCode

type HeartbeatStatusCode string
const (
	HeartbeatStatusCodeNotStarted  HeartbeatStatusCode = "NOT_STARTED"
	HeartbeatStatusCodeAlive       HeartbeatStatusCode = "ALIVE"
	HeartbeatStatusCodeDead        HeartbeatStatusCode = "DEAD"
	HeartbeatStatusCodeResurrected HeartbeatStatusCode = "RESURRECTED"
)

type LeveledLogger

type LeveledLogger interface {
	// Debugf logs a debug message using Printf conventions.
	Debugf(format string, v ...interface{})

	// Errorf logs a warning message using Printf conventions.
	Errorf(format string, v ...interface{})

	// Infof logs an informational message using Printf conventions.
	Infof(format string, v ...interface{})

	// Warnf logs a warning message using Printf conventions.
	Warnf(format string, v ...interface{})
}

LeveledLogger provides a basic leveled logging interface for printing debug, informational, warning, and error messages.

type License

type License struct {
	ID               string                 `json:"-"`
	Type             string                 `json:"-"`
	Name             string                 `json:"name"`
	Key              string                 `json:"key"`
	Expiry           *time.Time             `json:"expiry"`
	Scheme           SchemeCode             `json:"scheme"`
	RequireHeartbeat bool                   `json:"requireHeartbeat"`
	LastValidated    *time.Time             `json:"lastValidated"`
	Created          time.Time              `json:"created"`
	Updated          time.Time              `json:"updated"`
	Metadata         map[string]interface{} `json:"metadata"`
	PolicyId         string                 `json:"-"`
	LastValidation   *ValidationResult      `json:"-"`
}

License represents a Keygen license object.

func Validate

func Validate(fingerprints ...string) (*License, error)

Validate performs a license validation using the current Token, scoped to any provided fingerprints. The first fingerprint should be a machine fingerprint, and the rest are optional component fingerprints. It returns a License, and an error if the license is invalid, e.g. ErrLicenseNotActivated or ErrLicenseExpired.

func (*License) Activate

func (l *License) Activate(fingerprint string, components ...Component) (*Machine, error)

Activate performs a machine activation for the license, identified by the provided fingerprint. If the activation is successful, the new machine will be returned. An error will be returned if the activation fails, e.g. ErrMachineLimitExceeded or ErrMachineAlreadyActivated.

func (*License) Checkout

func (l *License) Checkout(options ...CheckoutOption) (*LicenseFile, error)

Checkout generates an encrypted license file. Returns a LicenseFile.

func (*License) Deactivate

func (l *License) Deactivate(id string) error

Deactivate performs a machine deactivation, identified by the provided ID. The ID can be the machine's UUID or the machine's fingerprint. An error will be returned if the machine deactivation fails.

func (*License) Entitlements

func (l *License) Entitlements() (Entitlements, error)

Machines lists up to 100 entitlements for the license.

func (*License) Machine

func (l *License) Machine(id string) (*Machine, error)

Machine retreives a machine, identified by the provided ID. The ID can be the machine's UUID or the machine's fingerprint. An error will be returned if it does not exist.

func (*License) Machines

func (l *License) Machines() (Machines, error)

Machines lists up to 100 machines for the license.

func (*License) SetData

func (l *License) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

func (*License) SetID

func (l *License) SetID(id string) error

SetID implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*License) SetRelationships

func (l *License) SetRelationships(relationships map[string]interface{}) error

SetRelationships implements the jsonapi.UnmarshalRelationship interface.

func (*License) SetType

func (l *License) SetType(t string) error

SetType implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*License) Validate

func (l *License) Validate(fingerprints ...string) error

Validate performs a license validation, scoped to an optional device fingerprint and an optional array of hardware component fingerprints. It returns an error if the license is invalid, e.g. ErrLicenseNotActivated, ErrLicenseExpired or ErrLicenseTooManyMachines.

func (*License) Verify

func (l *License) Verify() ([]byte, error)

Verify checks if the license's key is genuine by cryptographically verifying the key using your PublicKey. If the license is genuine, the decoded dataset from the key will be returned. An error will be returned if the license is not genuine, or if the key is not signed, e.g. ErrLicenseNotGenuine or ErrLicenseNotSigned.

type LicenseFile

type LicenseFile struct {
	ID          string    `json:"-"`
	Type        string    `json:"-"`
	Certificate string    `json:"certificate"`
	Issued      time.Time `json:"issued"`
	Expiry      time.Time `json:"expiry"`
	TTL         int       `json:"ttl"`
	LicenseID   string    `json:"-"`
}

LicenseFile represents a Keygen license file.

func (*LicenseFile) Decrypt

func (lic *LicenseFile) Decrypt(key string) (*LicenseFileDataset, error)

Decrypt decrypts the license file's encrypted dataset. It returns the decrypted dataset and any errors that occurred during decryption, e.g. ErrLicenseFileNotEncrypted.

func (*LicenseFile) SetData

func (lic *LicenseFile) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

func (*LicenseFile) SetID

func (lic *LicenseFile) SetID(id string) error

SetID implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*LicenseFile) SetRelationships

func (lic *LicenseFile) SetRelationships(relationships map[string]interface{}) error

SetRelationships implements the jsonapi.UnmarshalRelationship interface.

func (*LicenseFile) SetType

func (lic *LicenseFile) SetType(t string) error

SetType implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*LicenseFile) Verify

func (lic *LicenseFile) Verify() error

Decrypt verifies the license file's signature. It returns any errors that occurred during verification, e.g. ErrLicenseFileInvalid.

type LicenseFileDataset

type LicenseFileDataset struct {
	License      License      `json:"-"`
	Entitlements Entitlements `json:"-"`
	Issued       time.Time    `json:"issued"`
	Expiry       time.Time    `json:"expiry"`
	TTL          int          `json:"ttl"`
}

LicenseFileDataset represents a decrypted license file object.

func (*LicenseFileDataset) SetData

func (lic *LicenseFileDataset) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

func (*LicenseFileDataset) SetIncluded

func (lic *LicenseFileDataset) SetIncluded(relationships []*jsonapi.ResourceObject, unmarshal func(res *jsonapi.ResourceObject, target interface{}) error) error

SetIncluded implements jsonapi.UnmarshalIncluded interface.

func (*LicenseFileDataset) SetMeta

func (lic *LicenseFileDataset) SetMeta(to func(target interface{}) error) error

SetMeta implements jsonapi.UnmarshalMeta interface.

type LicenseFileError

type LicenseFileError struct{ Err error }

LicenseFileError represents an invalid license file error.

func (*LicenseFileError) Error

func (e *LicenseFileError) Error() string

func (*LicenseFileError) Unwrap

func (e *LicenseFileError) Unwrap() error

type LicenseKeyError

type LicenseKeyError struct{ Err *Error }

LicenseKeyError represents an API authentication error due to an invalid license key.

func (*LicenseKeyError) Error

func (e *LicenseKeyError) Error() string

func (*LicenseKeyError) Unwrap

func (e *LicenseKeyError) Unwrap() error

type LicenseTokenError

type LicenseTokenError struct{ Err *Error }

LicenseTokenError represents an API authentication error due to an invalid license token.

func (*LicenseTokenError) Error

func (e *LicenseTokenError) Error() string

func (*LicenseTokenError) Unwrap

func (e *LicenseTokenError) Unwrap() error

type LogLevel

type LogLevel uint32
const (
	LogLevelNone LogLevel = iota
	LogLevelError
	LogLevelWarn
	LogLevelInfo
	LogLevelDebug
)

type Machine

type Machine struct {
	ID                string                 `json:"-"`
	Type              string                 `json:"-"`
	Name              string                 `json:"name"`
	Fingerprint       string                 `json:"fingerprint"`
	Hostname          string                 `json:"hostname"`
	Platform          string                 `json:"platform"`
	Cores             int                    `json:"cores"`
	RequireHeartbeat  bool                   `json:"requireHeartbeat"`
	HeartbeatStatus   HeartbeatStatusCode    `json:"heartbeatStatus"`
	HeartbeatDuration int                    `json:"heartbeatDuration"`
	Created           time.Time              `json:"created"`
	Updated           time.Time              `json:"updated"`
	Metadata          map[string]interface{} `json:"metadata"`
	LicenseID         string                 `json:"-"`
	// contains filtered or unexported fields
}

Machine represents a Keygen machine object.

func (*Machine) Checkout

func (m *Machine) Checkout(options ...CheckoutOption) (*MachineFile, error)

Checkout generates an encrypted machine file. Returns a MachineFile.

func (*Machine) Components added in v2.8.0

func (m *Machine) Components() (Components, error)

Components lists up to 100 components for the machine.

func (*Machine) Deactivate

func (m *Machine) Deactivate() error

Deactivate performs a machine deactivation for the current Machine. An error will be returned if the machine deactivation fails.

func (Machine) GetData

func (m Machine) GetData() interface{}

GetData implements the jsonapi.MarshalData interface.

func (Machine) GetID

func (m Machine) GetID() string

GetID implements the jsonapi.MarshalResourceIdentifier interface.

func (Machine) GetType

func (m Machine) GetType() string

GetType implements the jsonapi.MarshalResourceIdentifier interface.

func (*Machine) Monitor

func (m *Machine) Monitor() error

Monitor performs, on a loop, a machine hearbeat ping for the current Machine. An error channel will be returned, where any ping errors will be emitted. Pings are sent according to the machine's required heartbeat window, minus 30 seconds to account for any network lag. Panics if a heartbeat ping fails after first ping.

func (*Machine) Processes

func (m *Machine) Processes() (Processes, error)

Processes lists up to 100 processes for the machine.

func (*Machine) SetData

func (m *Machine) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

func (*Machine) SetID

func (m *Machine) SetID(id string) error

SetID implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*Machine) SetType

func (m *Machine) SetType(t string) error

SetType implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*Machine) Spawn

func (m *Machine) Spawn(pid string) (*Process, error)

Spawn creates a new process for a machine, identified by the provided pid. If successful, the new Process will be returned. When unsuccessful, as error will be returned, e.g. ErrProcessLimitExceeded. Automatically starts a loop that sends heartbeat pings according to the process's Interval. Panics if a heartbeat ping fails after first ping.

type MachineFile

type MachineFile struct {
	ID          string    `json:"-"`
	Type        string    `json:"-"`
	Certificate string    `json:"certificate"`
	Issued      time.Time `json:"issued"`
	Expiry      time.Time `json:"expiry"`
	TTL         int       `json:"ttl"`
	MachineID   string    `json:"-"`
	LicenseID   string    `json:"-"`
}

MachineFile represents a Keygen license file.

func (*MachineFile) Decrypt

func (lic *MachineFile) Decrypt(key string) (*MachineFileDataset, error)

Decrypt decrypts the machine file's encrypted dataset. It returns the decrypted dataset and any errors that occurred during decryption, e.g. ErrMachineFileNotEncrypted.

func (*MachineFile) SetData

func (lic *MachineFile) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

func (*MachineFile) SetID

func (lic *MachineFile) SetID(id string) error

SetID implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*MachineFile) SetRelationships

func (lic *MachineFile) SetRelationships(relationships map[string]interface{}) error

SetRelationships implements the jsonapi.UnmarshalRelationship interface.

func (*MachineFile) SetType

func (lic *MachineFile) SetType(t string) error

SetType implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*MachineFile) Verify

func (lic *MachineFile) Verify() error

Decrypt verifies the machine file's signature. It returns any errors that occurred during verification, e.g. ErrMachineFileInvalid.

type MachineFileDataset

type MachineFileDataset struct {
	Machine      Machine      `json:"-"`
	License      License      `json:"-"`
	Entitlements Entitlements `json:"-"`
	Components   Components   `json:"-"`
	Issued       time.Time    `json:"issued"`
	Expiry       time.Time    `json:"expiry"`
	TTL          int          `json:"ttl"`
}

MachineFileDataset represents a decrypted machine file object.

func (*MachineFileDataset) SetData

func (lic *MachineFileDataset) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

func (*MachineFileDataset) SetIncluded

func (lic *MachineFileDataset) SetIncluded(relationships []*jsonapi.ResourceObject, unmarshal func(res *jsonapi.ResourceObject, target interface{}) error) error

SetIncluded implements jsonapi.UnmarshalIncluded interface.

func (*MachineFileDataset) SetMeta

func (lic *MachineFileDataset) SetMeta(to func(target interface{}) error) error

SetMeta implements jsonapi.UnmarshalMeta interface.

type MachineFileError

type MachineFileError struct{ Err error }

MachineFileError represents an invalid machine file error.

func (*MachineFileError) Error

func (e *MachineFileError) Error() string

func (*MachineFileError) Unwrap

func (e *MachineFileError) Unwrap() error

type Machines

type Machines []Machine

Machines represents an array of machine objects.

func (*Machines) SetData

func (m *Machines) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

type NotAuthorizedError

type NotAuthorizedError struct{ Err *Error }

NotAuthorizedError represents an API permission error.

func (*NotAuthorizedError) Error

func (e *NotAuthorizedError) Error() string

func (*NotAuthorizedError) Unwrap

func (e *NotAuthorizedError) Unwrap() error

type NotFoundError

type NotFoundError struct{ Err *Error }

NotFoundError represents an API not found error.

func (*NotFoundError) Error

func (e *NotFoundError) Error() string

func (*NotFoundError) Unwrap

func (e *NotFoundError) Unwrap() error

type Process

type Process struct {
	ID        string                 `json:"-"`
	Type      string                 `json:"-"`
	Pid       string                 `json:"pid"`
	Status    ProcessStatusCode      `json:"status"`
	Interval  int                    `json:"interval"`
	Created   time.Time              `json:"created"`
	Updated   time.Time              `json:"updated"`
	Metadata  map[string]interface{} `json:"metadata"`
	MachineID string                 `json:"-"`
}

Process represents a Keygen process object.

func (Process) GetData

func (p Process) GetData() interface{}

GetData implements the jsonapi.MarshalData interface.

func (Process) GetID

func (p Process) GetID() string

GetID implements the jsonapi.MarshalResourceIdentifier interface.

func (Process) GetType

func (p Process) GetType() string

GetType implements the jsonapi.MarshalResourceIdentifier interface.

func (*Process) Kill

func (p *Process) Kill() error

Kill deletes the current Process. An error will be returned if the process deletion fails.

func (*Process) SetData

func (p *Process) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

func (*Process) SetID

func (p *Process) SetID(id string) error

SetID implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*Process) SetType

func (p *Process) SetType(t string) error

SetType implements the jsonapi.UnmarshalResourceIdentifier interface.

type ProcessStatusCode

type ProcessStatusCode string
const (
	ProcessStatusCodeAlive ProcessStatusCode = "ALIVE"
	ProcessStatusCodeDead  ProcessStatusCode = "DEAD"
)

type Processes

type Processes []Process

Processes represents an array of process objects.

func (*Processes) SetData

func (p *Processes) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

type RateLimitError

type RateLimitError struct {
	Window     string
	Count      int
	Limit      int
	Remaining  int
	Reset      time.Time
	RetryAfter int
	Err        error
}

RateLimitError represents an API rate limiting error.

func (*RateLimitError) Error

func (e *RateLimitError) Error() string

func (*RateLimitError) Unwrap

func (e *RateLimitError) Unwrap() error

type Release

type Release struct {
	ID          string                 `json:"-"`
	Type        string                 `json:"-"`
	Name        string                 `json:"name"`
	Description string                 `json:"description"`
	Version     string                 `json:"version"`
	Channel     string                 `json:"channel"`
	Created     time.Time              `json:"created"`
	Updated     time.Time              `json:"updated"`
	Metadata    map[string]interface{} `json:"metadata"`
	// contains filtered or unexported fields
}

Release represents a Keygen release object.

func Upgrade

func Upgrade(options UpgradeOptions) (*Release, error)

Upgrade checks if an upgrade is available for the provided version. Returns a Release and any errors that occurred, e.g. ErrUpgradeNotAvailable.

func (*Release) Install

func (r *Release) Install() error

Install performs an update of the current executable to the new Release.

func (*Release) SetData

func (r *Release) SetData(to func(target interface{}) error) error

SetData implements the jsonapi.UnmarshalData interface.

func (*Release) SetID

func (r *Release) SetID(id string) error

SetID implements the jsonapi.UnmarshalResourceIdentifier interface.

func (*Release) SetType

func (r *Release) SetType(t string) error

SetType implements the jsonapi.UnmarshalResourceIdentifier interface.

type Response

type Response struct {
	Request  *http.Request
	ID       string
	Headers  http.Header
	Document *jsonapi.Document
	Size     int
	Body     []byte
	Status   int
}

type SchemeCode

type SchemeCode string
const (
	SchemeCodeEd25519 SchemeCode = "ED25519_SIGN"
)

type UpgradeOptions

type UpgradeOptions struct {
	// CurrentVersion is the current version of the program. This will be used by
	// Keygen to determine if an upgrade is available.
	CurrentVersion string

	// Product is the product ID to scope the upgrade to. This defaults to keygen.Product,
	// but overriding it may be useful if you're requesting an upgrade for another
	// accessible product, e.g. a product with an OPEN distribution strategy.
	Product string

	// Package is the package ID to scope the upgrade to. This defaults to keygen.Package,
	// but overriding it may be useful if you're requesting an upgrade for another
	// accessible package of the product.
	Package string

	// Constraint is a version constraint to use when checking for upgrades. For
	// example, to pin upgrades to v1, you would pass a "1.0" constraint.
	Constraint string

	// Channel is the release channel. One of: stable, rc, beta, alpha or dev.
	Channel string

	// PublicKey is your personal Ed25519ph public key, generated using Keygen's CLI
	// or using ssh-keygen. This will be used to verify the release's signature
	// before install. This MUST NOT be your Keygen account's public key.
	PublicKey string

	// Filename is the template string used when retrieving an artifact during
	// install. This should compile to a valid artifact identifier, e.g. a
	// filename for the current platform and arch.
	//
	// The default template is below:
	//
	//   {{.program}}_{{.platform}}_{{.arch}}{{if .ext}}.{{.ext}}{{end}}
	//
	// Available template variables:
	//
	//   program  // the name of the currently running program (i.e. basename of os.Args[0])
	//   ext      // the extension based on current platform (i.e. exe on Windows)
	//   platform // the current platform (i.e. GOOS)
	//   arch     // the current architecture (i.e. GOARCH)
	//   channel  // the release channel (e.g. stable)
	//   version  // the release version (e.g. 1.0.0-beta.3)
	//
	// If more control is needed, provide a string.
	Filename string
}

type ValidationCode

type ValidationCode string
const (
	ValidationCodeValid                    ValidationCode = "VALID"
	ValidationCodeNotFound                 ValidationCode = "NOT_FOUND"
	ValidationCodeSuspended                ValidationCode = "SUSPENDED"
	ValidationCodeExpired                  ValidationCode = "EXPIRED"
	ValidationCodeOverdue                  ValidationCode = "OVERDUE"
	ValidationCodeNoMachine                ValidationCode = "NO_MACHINE"
	ValidationCodeNoMachines               ValidationCode = "NO_MACHINES"
	ValidationCodeTooManyMachines          ValidationCode = "TOO_MANY_MACHINES"
	ValidationCodeTooManyCores             ValidationCode = "TOO_MANY_CORES"
	ValidationCodeTooManyProcesses         ValidationCode = "TOO_MANY_PROCESSES"
	ValidationCodeFingerprintScopeRequired ValidationCode = "FINGERPRINT_SCOPE_REQUIRED"
	ValidationCodeFingerprintScopeMismatch ValidationCode = "FINGERPRINT_SCOPE_MISMATCH"
	ValidationCodeFingerprintScopeEmpty    ValidationCode = "FINGERPRINT_SCOPE_EMPTY"
	ValidationCodeComponentsScopeRequired  ValidationCode = "COMPONENTS_SCOPE_REQUIRED"
	ValidationCodeComponentsScopeMismatch  ValidationCode = "COMPONENTS_SCOPE_MISMATCH"
	ValidationCodeComponentsScopeEmpty     ValidationCode = "COMPONENTS_SCOPE_EMPTY"
	ValidationCodeHeartbeatNotStarted      ValidationCode = "HEARTBEAT_NOT_STARTED"
	ValidationCodeHeartbeatDead            ValidationCode = "HEARTBEAT_DEAD"
	ValidationCodeProductScopeRequired     ValidationCode = "PRODUCT_SCOPE_REQUIRED"
	ValidationCodeProductScopeEmpty        ValidationCode = "PRODUCT_SCOPE_MISMATCH"
	ValidationCodePolicyScopeRequired      ValidationCode = "POLICY_SCOPE_REQUIRED"
	ValidationCodePolicyScopeMismatch      ValidationCode = "POLICY_SCOPE_MISMATCH"
	ValidationCodeMachineScopeRequired     ValidationCode = "MACHINE_SCOPE_REQUIRED"
	ValidationCodeMachineScopeMismatch     ValidationCode = "MACHINE_SCOPE_MISMATCH"
	ValidationCodeEntitlementsMissing      ValidationCode = "ENTITLEMENTS_MISSING"
	ValidationCodeEntitlementsEmpty        ValidationCode = "ENTITLEMENTS_SCOPE_EMPTY"
)

type ValidationResult added in v2.2.0

type ValidationResult struct {
	Detail string           `json:"detail"`
	Valid  bool             `json:"valid"`
	Code   ValidationCode   `json:"code"`
	Scope  *ValidationScope `json:"scope,omitempty"`
}

ValidationResult is the result of the validation.

type ValidationScope added in v2.8.0

type ValidationScope struct {
	// contains filtered or unexported fields
}

ValidationResult contains the scopes for a validation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL