Go SDK

SDK 2.1.1 offline migration: configure trusted tenant public keys independently of cache files. No pins means no cached authorization. Persisted offline helpers use the configured client and re-verify the original signed token on every read. Offline setup and migration.

Device and usage operations in SDK 2.1.1: all clients can request key-only device removal using tenant slug, key and activation ID. HTTP 204 confirms removal; protected portals still require OTP/account customer flows. Usage reporting returns boolean confirmation, supports nested/empty metadata objects and normalizes quantities below 1 to 1. Java/C++ use tryMeter/try_meter for that boolean contract and retain legacy detailed meter results. Neither operation retries automatically or uses offline cache. See this SDK's README for signatures and failure handling.

Floating sessions in SDK 2.1.1: checkout and heartbeat return a failed session without credentials on network/HTTP/parse failure (RequestFailed). Business denial preserves its error code or uses FloatingDenied. Floating never uses offline cache. The new boolean TryCheckin equivalent confirms HTTP 204; legacy checkin is best effort. Method names follow this language's conventions. See the source README and SDK availability matrix before choosing a registry version.

Official SDKs

Go SDK

SDK 2.1.1 request settings

A slow or unavailable license server must not leave your application waiting indefinitely. All eight SDKs default to a 10-second timeout for each attempt and two additional attempts for safe requests. The usual backoff is 250 milliseconds, then 500 milliseconds. This is not a single 10-second deadline for the complete operation: activation also fetches a nonce.

  • License validation, nonce/public-key retrieval and floating heartbeat may retry a network failure or HTTP 5xx response. Set the retry count to zero to disable these retries; the range is 0–5.
  • Floating checkout, activation POST, usage reporting, device removal and checkin are sent once. A lost response can follow a successful server change, so retrying these blindly can duplicate work.
  • HTTP 3xx/4xx responses and malformed successful responses do not retry. SDK-owned transports do not follow redirects, so configure the final HTTPS API address directly.

For validate/activate, branch on the server's error code when available. Otherwise HttpError means an HTTP rejection, InvalidResponse means malformed successful license/nonce evidence and RequestFailed means the network/5xx attempts were exhausted without a usable signed cache. Rejection or malformed successful evidence never becomes cached authorization. A network outage can use only a separately trusted, unexpired proof.

SDKConfiguration
GoOptions.Timeout uses time.Duration. MaxRetries is an optional integer pointer: nil defaults to two; a pointer to zero disables retries.

Timeout mechanisms follow each runtime: Python uses socket I/O timeouts; Unity delegates to its transport. Caller cancellation is separate from retry policy. Unity's default adapter disables redirects; WebGL needs Unity 2021.3 or later for that protection and fails closed on older versions. Portable .NET tests do not certify an Editor, IL2CPP or browser build. Verify your actual target before shipping.

Install SDK 2.1.1 using the SDK installation references and follow the offline migration guide when upgrading from 1.x.

Pure Go 1.21+ client, standard library only — crypto/ecdsa handles offline token verification natively, so unlike SDKs built on OpenSSL/PHP-openssl/Python cryptography this needs no raw-signature-to-DER conversion step. No third-party packages at all.

Download Go SDK (ZIP)

License: the Go SDK source is MIT-licensed by permitcore. Keep its notice when redistributing. See the Go SDK license. Existing 2.1.1 module archives are immutable; an older archive without the file can use the repository notice. This does not change SDK behavior or license the hosted service.

Installation

Published as a real, tagged Go module — go get works directly, no manual setup:

SDK 2.1.1 uses the /v2 module path. Install it with this command.

go get github.com/permitCore-spec/permitcore-sdk-go/v2@v2.1.1

Or, to work from the source ZIP above instead (e.g. for local edits before a release), add a replace directive to your own go.mod pointing at the extracted folder:

require github.com/permitCore-spec/permitcore-sdk-go/v2 v2.1.1

replace github.com/permitCore-spec/permitcore-sdk-go/v2 => ./permitcore-sdk-go

Quick start — validate

import "github.com/permitCore-spec/permitcore-sdk-go/v2"

client := permitcore.New("https://api.permitcore.dev")
result := client.Validate("PERMIT-XXXX-XXXX-XXXX-XXXX", "",
	"7f3e9a2b-1c4d-4e8f-9a6b-3d2c1e0f5a8b") // expectedProductID — required trailing arg, pass "" to omit

if result.ErrorCode == "WrongProduct" {
	log.Fatalf("This key is for a different product: %s", result.ProductID)
}
if !result.IsValid {
	log.Fatalf("License invalid: %s", result.Message)
}

if result.HasFeature("export") {
	enableExport()
}

Activate (call once per installation)

result := client.Activate(
	"PERMIT-XXXX-XXXX-XXXX-XXXX",
	"",                   // deviceId — auto-generated HWID when empty
	"Production Server #1", // deviceName
	"",                   // version, optional
	"7f3e9a2b-1c4d-4e8f-9a6b-3d2c1e0f5a8b", // expectedProductID — required trailing arg, pass "" to omit
)
if result.ErrorCode == "WrongProduct" {
	log.Fatalf("Wrong product: %s", result.ProductID)
}
if !result.IsValid {
	log.Fatalf("Activation failed: %s", result.Message)
}
// Save result.IsValid to local storage — use Validate() on every subsequent launch.

Metered billing

// Record a single event
client.Meter(licenseKey, "api_call", 1, nil)

// Record bulk usage with metadata
client.Meter(licenseKey, "export", 5, map[string]any{"format": "pdf", "pages": 12})

Floating licenses

// Check out a seat at session start
session := client.Checkout(licenseKey, "", "")
if !session.Success {
	log.Fatalf("No seats available: %s", session.Message)
}
token := session.SessionToken

// Heartbeat every 4-5 minutes to keep the seat alive
client.Heartbeat(token)

// Release the seat when done
client.Checkin(token)

Offline grace period

result := client.Validate(licenseKey, "", "") // falls back to disk cache automatically (expectedProductID omitted here)

if result.IsOffline {
	// Running on cached result — server unreachable
	showNotice("Running offline. Connect to refresh your license.")
}

Offline license tokens

No extra package needed — crypto/ecdsa is standard library. Verification is pure local ECDSA P-256/SHA-256, zero network calls:

result := permitcore.VerifyOfflineToken(token, publicKeyBase64)

if result.IsValid {
	fmt.Println("Valid! Product:", result.Payload.ProductName)
}

// Bind to this device + persist locally (call once, e.g. at install time)
result = client.ActivateOffline(token, deviceID, productID)

// On every later launch — no token needed, reads the local cache
result = client.ValidateOffline(deviceID, productID)

LicenseResult reference

FieldTypeDescription
IsValidboolTrue if the license is active and valid
ProductNamestringProduct the license belongs to
RemainingActivations*intActivation slots remaining
ExpiresAtstringISO 8601 expiry date, empty = perpetual
Features[]stringFeature flag list, e.g. ["export","api"]
IsTrialboolTrue for trial licenses
TrialDaysRemaining*intDays until trial expires
NodeLockedboolTrue if bound to a specific device
OfflineGraceDays*intHow many days the cache remains valid
MinVersion / MaxVersionstringVersion enforcement bounds
VendorWarningstringNon-fatal message from the vendor
MessagestringReason when IsValid == false
IsOfflineboolTrue when result came from local cache
ErrorCodestringStable, machine-readable failure reason
ProductIDstringStable ID of the product this license belongs to. Always present when a license was found, even without passing expectedProductID.

HasFeature(feature string) bool — case-insensitive check whether a feature flag is present on the license.

Product scoping: by default, any active key belonging to your tenant validates/activates successfully regardless of which product it was issued for. Both Validate and Activate take a trailing expectedProductID string argument (pass "" to omit) — set it and the server rejects a mismatch with ErrorCode == "WrongProduct". Find a product's ID in the Admin panel under Products.

Try it live: the downloadable Task Manager Pro demo includes a working Go build gated behind a real license, plus a developer console panel that exercises every SDK call (validate, activate, offline tokens, floating checkout) against your own PermitCore instance.