{"id":28515957,"url":"https://github.com/keygen-sh/keygen-go","last_synced_at":"2025-07-05T17:30:58.039Z","repository":{"id":40737087,"uuid":"415937291","full_name":"keygen-sh/keygen-go","owner":"keygen-sh","description":"Keygen reference SDK for Go. Integrate license activation, automatic updates and offline licensing for Go binaries.","archived":false,"fork":false,"pushed_at":"2025-02-05T16:16:19.000Z","size":210,"stargazers_count":127,"open_issues_count":1,"forks_count":22,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-30T00:38:32.463Z","etag":null,"topics":["auto-updater","license-activation","license-keys","licensing","software-distribution","software-licensing"],"latest_commit_sha":null,"homepage":"https://keygen.sh","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/keygen-sh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"ezekg"}},"created_at":"2021-10-11T13:25:01.000Z","updated_at":"2025-06-20T19:34:13.000Z","dependencies_parsed_at":"2025-02-05T17:34:15.067Z","dependency_job_id":null,"html_url":"https://github.com/keygen-sh/keygen-go","commit_stats":null,"previous_names":["keygen-sh/sdk"],"tags_count":102,"template":false,"template_full_name":null,"purl":"pkg:github/keygen-sh/keygen-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keygen-sh%2Fkeygen-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keygen-sh%2Fkeygen-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keygen-sh%2Fkeygen-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keygen-sh%2Fkeygen-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/keygen-sh","download_url":"https://codeload.github.com/keygen-sh/keygen-go/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keygen-sh%2Fkeygen-go/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263778331,"owners_count":23509974,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["auto-updater","license-activation","license-keys","licensing","software-distribution","software-licensing"],"created_at":"2025-06-09T03:31:26.888Z","updated_at":"2025-07-05T17:30:58.029Z","avatar_url":"https://github.com/keygen-sh.png","language":"Go","funding_links":["https://github.com/sponsors/ezekg"],"categories":["Go"],"sub_categories":[],"readme":"# Keygen Go SDK\n\n[![godoc reference](https://godoc.org/github.com/keygen-sh/keygen-go/v3?status.png)](https://godoc.org/github.com/keygen-sh/keygen-go/v3)\n[![CI](https://github.com/keygen-sh/keygen-go/actions/workflows/test.yml/badge.svg)](https://github.com/keygen-sh/keygen-go/actions)\n\nPackage [`keygen`](https://pkg.go.dev/github.com/keygen-sh/keygen-go/v3) allows Go programs to\nlicense and remotely update themselves using the [keygen.sh](https://keygen.sh) service.\n\n## Installing\n\n```\ngo get github.com/keygen-sh/keygen-go/v3\n```\n\n## Config\n\n### keygen.Account\n\n`Account` is your Keygen account ID used globally in the SDK. All requests will be made\nto this account. This should be hard-coded into your app.\n\n```go\nkeygen.Account = \"1fddcec8-8dd3-4d8d-9b16-215cac0f9b52\"\n```\n\n### keygen.Product\n\n`Product` is your Keygen product ID used globally in the SDK. All license validations and\nupgrade requests will be scoped to this product. This should be hard-coded into your app.\n\n```go\nkeygen.Product = \"1f086ec9-a943-46ea-9da4-e62c2180c2f4\"\n```\n\n### keygen.LicenseKey\n\n`LicenseKey` is a license key belonging to the end-user (licensee). This will be used for license\nvalidations, activations, deactivations and upgrade requests. You will need to prompt the\nend-user for this value.\n\nYou will need to set the license policy's authentication strategy to `LICENSE` or `MIXED`.\n\nSetting `LicenseKey` will take precedence over `Token`.\n\n```go\nkeygen.LicenseKey = \"C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3\"\n```\n\n### keygen.Token\n\n`Token` is an activation token belonging to the licensee. This will be used for license validations,\nactivations, deactivations and upgrade requests. You will need to prompt the end-user for this.\n\nYou will need to set the license policy's authentication strategy to `TOKEN` or `MIXED`.\n\n```go\nkeygen.Token = \"activ-d66e044ddd7dcc4169ca9492888435d3v3\"\n```\n\n### keygen.PublicKey\n\n`PublicKey` is your Keygen account's hex-encoded Ed25519 public key, used for verifying signed license keys\nand API response signatures. When set, API response signatures will automatically be verified. You may\nleave it blank to skip verifying response signatures. This should be hard-coded into your app.\n\n```go\nkeygen.PublicKey = \"e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788\"\n```\n\n### keygen.Logger\n\n`Logger` is a leveled logger implementation used for printing debug, informational, warning, and\nerror messages. The default log level is `LogLevelError`. You may provide your own logger which\nimplements `LeveledLogger`.\n\n```go\nkeygen.Logger = \u0026CustomLogger{Level: keygen.LogLevelDebug}\n```\n\n## Usage\n\nThe following top-level functions are available. We recommend starting here.\n\n### keygen.Validate(ctx, fingerprints ...string)\n\nTo validate a license, configure `keygen.Account` and `keygen.Product` with your Keygen account\ndetails. Then prompt the end-user for their license key or token and set `keygen.LicenseKey`\nor `keygen.Token`, respectively.\n\nThe `Validate` method accepts zero or more fingerprints, which can be used to scope a\nlicense validation to a particular device fingerprint and its hardware components.\nThe first fingerprint should be a machine fingerprint, and the rest are optional\ncomponent fingerprints.\n\nIt will return a `License` object as well as any validation errors that occur. The `License`\nobject can be used to perform additional actions, such as `license.Activate(fingerprint)`.\n\n```go\nlicense, err := keygen.Validate(context.Background(), fingerprint)\nswitch {\ncase err == keygen.ErrLicenseNotActivated:\n  panic(\"license is not activated!\")\ncase err == keygen.ErrLicenseExpired:\n  panic(\"license is expired!\")\ncase err != nil:\n  panic(\"license is invalid!\")\n}\n\nfmt.Println(\"License is valid!\")\n```\n\n### keygen.Upgrade(ctx, options keygen.UpgradeOptions)\n\nCheck for an upgrade. When an upgrade is available, a `Release` will be returned which will\nallow the update to be installed, replacing the currently running binary. When an upgrade\nis not available, an `ErrUpgradeNotAvailable` error will be returned indicating the current\nversion is up-to-date.\n\nWhen a `PublicKey` is provided, and the release has a `Signature`, the signature will be\ncryptographically verified using Ed25519ph before installing. The `PublicKey` MUST be a\npersonal Ed25519ph public key. It MUST NOT be your Keygen account's public key (method\nwill panic if public keys match).\n\nYou can read more about generating a personal keypair and about code signing [here](https://keygen.sh/docs/cli/#code-signing).\n\n```go\nopts := keygen.UpgradeOptions{CurrentVersion: \"1.0.0\", Channel: \"stable\", PublicKey: \"5ec69b78d4b5d4b624699cef5faf3347dc4b06bb807ed4a2c6740129f1db7159\"}\n\nctx := context.Background()\n\n// Check for an upgrade\nrelease, err := keygen.Upgrade(ctx, opts)\nswitch {\ncase err == keygen.ErrUpgradeNotAvailable:\n  fmt.Println(\"No upgrade available, already at the latest version!\")\n\n  return\ncase err != nil:\n  fmt.Println(\"Upgrade check failed!\")\n\n  return\n}\n\n// Install the upgrade\nif err := release.Install(ctx); err != nil {\n  panic(\"upgrade install failed!\")\n}\n\nfmt.Println(\"Upgrade complete! Please restart.\")\n```\n\nTo quickly generate a keypair, use [Keygen's CLI](https://github.com/keygen-sh/keygen-cli):\n\n```bash\nkeygen genkey\n```\n\n## Examples\n\nBelow are various implementation examples, covering common licensing scenarios and use cases.\n\n### License Activation\n\nValidate the license for a particular device fingerprint, and activate when needed. We're\nusing [`machineid`](https://github.com/keygen-sh/machineid) for fingerprinting, which\nis cross-platform, using the operating system's native GUID.\n\n```go\npackage main\n\nimport (\n  \"context\"\n\n  \"github.com/keygen-sh/keygen-go/v3\"\n  \"github.com/keygen-sh/machineid\"\n)\n\nfunc main() {\n  keygen.Account = \"YOUR_KEYGEN_ACCOUNT_ID\"\n  keygen.Product = \"YOUR_KEYGEN_PRODUCT_ID\"\n  keygen.LicenseKey = \"A_KEYGEN_LICENSE_KEY\"\n\n  fingerprint, err := machineid.ProtectedID(keygen.Product)\n  if err != nil {\n    panic(err)\n  }\n\n  ctx := context.Background()\n\n  // Validate the license for the current fingerprint\n  license, err := keygen.Validate(ctx, fingerprint)\n  switch {\n  case err == keygen.ErrLicenseNotActivated:\n    // Activate the current fingerprint\n    machine, err := license.Activate(ctx, fingerprint)\n    switch {\n    case err == keygen.ErrMachineLimitExceeded:\n      panic(\"machine limit has been exceeded!\")\n    case err != nil:\n      panic(\"machine activation failed!\")\n    }\n  case err == keygen.ErrLicenseExpired:\n    panic(\"license is expired!\")\n  case err != nil:\n    panic(\"license is invalid!\")\n  }\n\n  fmt.Println(\"License is activated!\")\n}\n```\n\n### Automatic Upgrades\n\nCheck for an upgrade and automatically replace the current binary with the newest version.\n\n```go\npackage main\n\nimport (\n  \"context\"\n\n  \"github.com/keygen-sh/keygen-go/v3\"\n)\n\n// The current version of the program\nconst CurrentVersion = \"1.0.0\"\n\nfunc main() {\n  keygen.PublicKey = \"YOUR_KEYGEN_PUBLIC_KEY\"\n  keygen.Account = \"YOUR_KEYGEN_ACCOUNT_ID\"\n  keygen.Product = \"YOUR_KEYGEN_PRODUCT_ID\"\n  keygen.LicenseKey = \"A_KEYGEN_LICENSE_KEY\"\n\n  fmt.Printf(\"Current version: %s\\n\", CurrentVersion)\n  fmt.Println(\"Checking for upgrades...\")\n\n  ctx := context.Background()\n\n  opts := keygen.UpgradeOptions{CurrentVersion: CurrentVersion, Channel: \"stable\", PublicKey: \"YOUR_COMPANY_PUBLIC_KEY\"}\n\n  // Check for upgrade\n  release, err := keygen.Upgrade(ctx, opts)\n  switch {\n  case err == keygen.ErrUpgradeNotAvailable:\n    fmt.Println(\"No upgrade available, already at the latest version!\")\n\n    return\n  case err != nil:\n    fmt.Println(\"Upgrade check failed!\")\n\n    return\n  }\n\n  fmt.Printf(\"Upgrade available! Newest version: %s\\n\", release.Version)\n  fmt.Println(\"Installing upgrade...\")\n\n  // Download the upgrade and install it\n  err = release.Install(ctx)\n  if err != nil {\n    panic(\"upgrade install failed!\")\n  }\n\n  fmt.Printf(\"Upgrade complete! Installed version: %s\\n\", release.Version)\n  fmt.Println(\"Restart to finish installation...\")\n}\n```\n\n### Monitor Machine Heartbeats\n\nMonitor a machine's heartbeat, and automatically deactivate machines in case of a crash\nor an unresponsive node. We recommend using a random UUID fingerprint for activating\nnodes in cloud-based scenarios, since nodes may share underlying hardware.\n\n```go\npackage main\n\nimport (\n  \"context\"\n\n  \"github.com/google/uuid\"\n  \"github.com/keygen-sh/keygen-go/v3\"\n)\n\nfunc main() {\n  keygen.Account = \"YOUR_KEYGEN_ACCOUNT_ID\"\n  keygen.Product = \"YOUR_KEYGEN_PRODUCT_ID\"\n  keygen.LicenseKey = \"A_KEYGEN_LICENSE_KEY\"\n\n  // The current device's fingerprint (could be e.g. MAC, mobo ID, GUID, etc.)\n  fingerprint := uuid.New().String()\n\n  ctx := context.Background()\n\n  // Keep our example process alive\n  done := make(chan bool, 1)\n\n  // Validate the license for the current fingerprint\n  license, err := keygen.Validate(ctx, fingerprint)\n  switch {\n  case err == keygen.ErrLicenseNotActivated:\n    // Activate the current fingerprint\n    machine, err := license.Activate(ctx, fingerprint)\n    if err != nil {\n      fmt.Println(\"machine activation failed!\")\n\n      panic(err)\n    }\n\n    // Handle SIGINT and gracefully deactivate the machine\n    sigs := make(chan os.Signal, 1)\n\n    signal.Notify(sigs, os.Interrupt)\n\n    go func() {\n      for sig := range sigs {\n        fmt.Printf(\"Caught %v, deactivating machine and gracefully exiting...\\n\", sig)\n\n        if err := machine.Deactivate(ctx); err != nil {\n          panic(err)\n        }\n\n        fmt.Println(\"Machine was deactivated!\")\n        fmt.Println(\"Exiting...\")\n\n        done \u003c- true\n      }\n    }()\n\n    // Start a heartbeat monitor for the current machine\n    if err := machine.Monitor(ctx); err != nil {\n      fmt.Println(\"Machine heartbeat monitor failed to start!\")\n\n      panic(err)\n    }\n\n    fmt.Println(\"Machine is activated and monitored!\")\n  case err != nil:\n    fmt.Println(\"License is invalid!\")\n\n    panic(err)\n  }\n\n  fmt.Println(\"License is valid!\")\n\n  \u003c-done\n}\n```\n\n### Offline License Files\n\nCryptographically verify and decrypt an encrypted license file. This is useful for checking if a license\nfile is genuine in offline or air-gapped environments. Returns the license file's dataset and any\nerrors that occurred during verification and decryption, e.g. `ErrLicenseFileNotGenuine`.\n\nWhen decrypting a license file, you MUST provide the license's key as the decryption key.\n\nWhen initializing a `LicenseFile`, `Certificate` is required.\n\nRequires that `keygen.PublicKey` is set.\n\n```go\npackage main\n\nimport \"github.com/keygen-sh/keygen-go/v3\"\n\nfunc main() {\n  keygen.PublicKey = \"YOUR_KEYGEN_PUBLIC_KEY\"\n\n  // Read the license file\n  cert, err := ioutil.ReadFile(\"/etc/example/license.lic\")\n  if err != nil {\n    panic(\"license file is missing\")\n  }\n\n  // Verify the license file's signature\n  lic := \u0026keygen.LicenseFile{Certificate: string(cert)}\n  err = lic.Verify()\n  switch {\n  case err == keygen.ErrLicenseFileNotGenuine:\n    panic(\"license file is not genuine!\")\n  case err != nil:\n    panic(err)\n  }\n\n  // Use the license key to decrypt the license file\n  dataset, err := lic.Decrypt(\"A_KEYGEN_LICENSE_KEY\")\n  switch {\n  case err == keygen.ErrSystemClockUnsynced:\n    panic(\"system clock tampering detected!\")\n  case err == keygen.ErrLicenseFileExpired:\n    panic(\"license file is expired!\")\n  case err != nil:\n    panic(err)\n  }\n\n  fmt.Println(\"License file is genuine!\")\n  fmt.Printf(\"Decrypted dataset: %v\\n\", dataset)\n}\n```\n\n### Offline License Keys\n\nCryptographically verify and decode a signed license key. This is useful for checking if a license\nkey is genuine in offline or air-gapped environments. Returns the key's decoded dataset and any\nerrors that occurred during cryptographic verification, e.g. `ErrLicenseKeyNotGenuine`.\n\nWhen initializing a `License`, `Scheme` and `Key` are required.\n\nRequires that `keygen.PublicKey` is set.\n\n```go\npackage main\n\nimport \"github.com/keygen-sh/keygen-go/v3\"\n\nfunc main() {\n  keygen.PublicKey = \"YOUR_KEYGEN_PUBLIC_KEY\"\n\n  // Verify the license key's signature and decode embedded dataset\n  license := \u0026keygen.License{Scheme: keygen.SchemeCodeEd25519, Key: \"A_SIGNED_KEYGEN_LICENSE_KEY\"}\n  dataset, err := license.Verify()\n  switch {\n  case err == keygen.ErrLicenseKeyNotGenuine:\n    panic(\"license key is not genuine!\")\n  case err != nil:\n    panic(err)\n  }\n\n  fmt.Println(\"License is genuine!\")\n  fmt.Printf(\"Decoded dataset: %s\\n\", dataset)\n}\n```\n\n### Verify Webhooks\n\nWhen listening for webhook events from Keygen, you can verify requests came from\nKeygen's servers by using `keygen.VerifyWebhook`. This protects your webhook\nendpoint from event forgery and replay attacks.\n\nRequires that `keygen.PublicKey` is set.\n\n```go\npackage main\n\nimport (\n  \"log\"\n  \"net/http\"\n\n  \"github.com/keygen-sh/keygen-go/v3\"\n)\n\nfunc main() {\n  keygen.PublicKey = \"YOUR_KEYGEN_PUBLIC_KEY\"\n\n  http.HandleFunc(\"/webhooks\", func(w http.ResponseWriter, r *http.Request) {\n    if err := keygen.VerifyWebhook(r); err != nil {\n      w.WriteHeader(http.StatusBadRequest)\n\n      return\n    }\n\n    w.WriteHeader(http.StatusNoContent)\n  })\n\n  log.Fatal(http.ListenAndServe(\":8081\", nil))\n}\n```\n\n## Error Handling\n\nOur SDK tries to return meaningful errors which can be handled in your integration. Below\nare a handful of error recipes that can be used for the more common errors.\n\n### Invalid License Key\n\nWhen authenticating with a license key, you may receive a `LicenseKeyError` when the license\nkey does not exist. You can handle this accordingly.\n\n```go\npackage main\n\nimport (\n  \"context\"\n\n  \"github.com/keygen-sh/keygen-go/v3\"\n)\n\nfunc getLicense(ctx context.Context) (*keygen.License, error) {\n  keygen.LicenseKey = promptForLicenseKey()\n\n  license, err := keygen.Validate(ctx)\n  if err != nil {\n    if _, ok := err.(*keygen.LicenseKeyError); ok {\n      fmt.Println(\"License key does not exist!\")\n\n      return getLicense(ctx)\n    }\n\n    return nil, err\n  }\n\n  return license, nil\n}\n\nfunc main() {\n  keygen.Account = \"...\"\n  keygen.Product = \"...\"\n\n  ctx := context.Background()\n\n  license, err := getLicense(ctx)\n  if err != nil {\n    panic(err)\n  }\n\n  fmt.Printf(\"License: %v\\n\", license)\n}\n```\n\n### Invalid License Token\n\nWhen authenticating with a license token, you may receive a `LicenseTokenError` when the license\ntoken does not exist or has expired. You can handle this accordingly.\n\n```go\npackage main\n\nimport \"github.com/keygen-sh/keygen-go/v3\"\n\nfunc getLicense(ctx context.Context) (*keygen.License, error) {\n  keygen.Token = promptForLicenseToken()\n\n  license, err := keygen.Validate(ctx)\n  if err != nil {\n    if _, ok := err.(*keygen.LicenseTokenError); ok {\n      fmt.Println(\"License token does not exist!\")\n\n      return getLicense(ctx)\n    }\n\n    return nil, err\n  }\n\n  return license, nil\n}\n\nfunc main() {\n  keygen.Account = \"...\"\n  keygen.Product = \"...\"\n\n  ctx := context.Background()\n\n  license, err := getLicense(ctx)\n  if err != nil {\n    panic(err)\n  }\n\n  fmt.Printf(\"License: %v\\n\", license)\n}\n```\n\n### Rate Limiting\n\nWhen your integration makes too many requests too quickly, the IP address may be [rate limited](https://keygen.sh/docs/api/rate-limiting/).\nYou can handle this via the `RateLimitError` error. For example, you could use this error to\ndetermine how long to wait before retrying a request.\n\n```go\npackage main\n\nimport \"github.com/keygen-sh/keygen-go/v3\"\n\nfunc validate(ctx context.Context) (*keygen.License, error) {\n  license, err := keygen.Validate(ctx)\n  if err != nil {\n    if e, ok := err.(*keygen.RateLimitError); ok {\n      // Sleep until our rate limit window is passed\n      time.Sleep(time.Duration(e.RetryAfter) * time.Second)\n\n      // Retry validate\n      return validate(ctx)\n    }\n\n    return nil, err\n  }\n\n  return license, nil\n}\n\nfunc main() {\n  keygen.Account = \"YOUR_KEYGEN_ACCOUNT_ID\"\n  keygen.Product = \"YOUR_KEYGEN_PRODUCT_ID\"\n  keygen.LicenseKey = \"A_KEYGEN_LICENSE_KEY\"\n\n  ctx := context.Background()\n\n  license, err := validate(ctx)\n  if err != nil {\n    panic(err)\n  }\n\n  fmt.Printf(\"License: %v\\n\", license)\n}\n```\n\nYou may want to add a limit to the number of retry attempts.\n\n### Automatic retries\n\nWhen your integration has less-than-stellar network connectivity, or you simply want to\nensure that failed requests are retried, you can utilize a package such as [`retryablehttp`](https://github.com/hashicorp/go-retryablehttp)\nto implement automatic retries.\n\n```go\npackage main\n\nimport (\n  \"context\"\n\n  \"github.com/hashicorp/go-retryablehttp\"\n  \"github.com/keygen-sh/keygen-go/v3\"\n)\n\nfunc main() {\n  c := retryablehttp.NewClient()\n\n  // Configure with a jitter backoff and max attempts\n  c.Backoff = retryablehttp.LinearJitterBackoff\n  c.RetryMax = 5\n\n  keygen.HTTPClient = c.StandardClient()\n  keygen.Account = \"YOUR_KEYGEN_ACCOUNT_ID\"\n  keygen.Product = \"YOUR_KEYGEN_PRODUCT_ID\"\n  keygen.LicenseKey = \"A_KEYGEN_LICENSE_KEY\"\n\n  ctx := context.Background()\n\n  // Use SDK as you would normally\n  keygen.Validate(ctx)\n}\n```\n\n## Testing\n\nWhen implementing a testing strategy for your licensing integration, we recommend that you\nfully mock our APIs. This is especially important for CI/CD environments, to prevent\nunneeded load on our servers. Mocking our APIs will also allow you to more easily\nstay within your account's daily request limits.\n\nTo do so in Go, you can utilize [`gock`](https://github.com/h2non/gock) or [`httptest`](https://pkg.go.dev/net/http/httptest).\n\n```go\npackage main\n\nimport (\n  \"context\"\n  \"testing\"\n\n  \"github.com/keygen-sh/keygen-go/v3\"\n  \"gopkg.in/h2non/gock.v1\"\n)\n\nfunc init() {\n  keygen.PublicKey = \"e8601e48b69383ba520245fd07971e983d06d22c4257cfd82304601479cee788\"\n  keygen.Account = \"1fddcec8-8dd3-4d8d-9b16-215cac0f9b52\"\n  keygen.Product = \"1f086ec9-a943-46ea-9da4-e62c2180c2f4\"\n  keygen.LicenseKey = \"C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3\"\n}\n\nfunc TestExample(t *testing.T) {\n  ctx := context.Background()\n  defer gock.Off()\n\n  // Intercept Keygen's HTTP client\n  gock.InterceptClient(keygen.HTTPClient)\n  defer gock.RestoreClient(keygen.HTTPClient)\n\n  // Mock endpoints\n  gock.New(\"https://api.keygen.sh\").\n    Get(`/v1/accounts/([^\\/]+)/me`).\n    Reply(200).\n    SetHeader(\"Keygen-Signature\", `keyid=\"1fddcec8-8dd3-4d8d-9b16-215cac0f9b52\", algorithm=\"ed25519\", signature=\"IiyYX1ah2HFzbcCx+3sv+KJpOppFdMRuZ7NWlnwZMKAf5khj9c4TO4z6fr62BqNXlyROOTxZinX8UpXHJHVyAw==\", headers=\"(request-target) host date digest\"`).\n    SetHeader(\"Digest\", \"sha-256=d4uZ26hjiUNqopuSkYcYwg2aBuNtr4D1/9iDhlvf0H8=\").\n    SetHeader(\"Date\", \"Wed, 15 Jun 2022 18:52:14 GMT\").\n    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\"}}}`)\n\n  gock.New(\"https://api.keygen.sh\").\n    Post(`/v1/accounts/([^\\/]+)/licenses/([^\\/]+)/actions/validate`).\n    Reply(200).\n    SetHeader(\"Keygen-Signature\", `keyid=\"1fddcec8-8dd3-4d8d-9b16-215cac0f9b52\", algorithm=\"ed25519\", signature=\"18+5Q4749BKuUz9/f35UrdP5g3Pyt32pPN3J8e5BqSlRbqiXnz0HwtqbP5sbvGkq1yixelwgV6bcJ0WUtpDSBw==\", headers=\"(request-target) host date digest\"`).\n    SetHeader(\"Digest\", \"sha-256=c1y1CVVLG0mvt0MP1SJy/bOiNjCytxMOuHUhlCXXVVk=\").\n    SetHeader(\"Date\", \"Thu, 09 Feb 2023 21:20:13 GMT\").\n    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\"}}`)\n\n  // Allow old response signatures\n  keygen.MaxClockDrift = -1\n\n  // Use SDK as you would normally\n  _, err := keygen.Validate(ctx)\n  if err != nil {\n    t.Fatalf(\"Should not fail mock validation: err=%v\", err)\n  }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeygen-sh%2Fkeygen-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkeygen-sh%2Fkeygen-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeygen-sh%2Fkeygen-go/lists"}