abr_sdk.keygen#

Keygen HTTPS client for on-device license activation.

A device activates by trading its license key plus a hardware fingerprint for a signed machine file (the envelope), which the native SDK then verifies and installs. Activation is three Keygen calls, wrapped by

meth:

KeygenClient.activate:

  1. validate-key - confirm the key is valid; learn the license id.

  2. machine - find this fingerprint’s machine, or register a new one.

  3. check-out - download the signed machine file for that machine.

All requests use Keygen’s JSON:API dialect (application/vnd.api+json) and the customer-facing Authorization: License <key> scheme. The request and response shape consumed at each step is documented on the method that makes the call; only the fields named there are read. The downloaded envelope is handed to the SDK as-is; this module does not inspect or verify its contents.

Author: Trevor Bekolay (Applied Brain Research)

Module Contents#

Classes#

Failure

Coarse activation-failure category, mapped to a CLI exit code by the caller.

_Response

One HTTP response: status code and parsed JSON object (None if not an object).

KeygenClient

Customer-facing Keygen client bound to one license key and environment.

Functions#

unwrap_machine_file_pem

Strip the PEM machine-file markers and base64-decode the body.

_parse_json_object

Parse bytes as a JSON object; return None for non-objects or parse errors.

_first_error_detail

Return a short reason from the first JSON:API error object, or ‘’.

_resource_id

Return data.id from a JSON:API single-resource body, if present.

_match_machine_id

Return the id of the machine in rows matching fingerprint, or None.

Data#

API#

abr_sdk.keygen.DEFAULT_API_BASE: Final[str]#

‘https://api.keygen.sh/v1’

abr_sdk.keygen._KEYGEN_ACCOUNT_ID: Final[str]#

‘721630ba-ba6b-466b-a558-5b74f7f27b17’

abr_sdk.keygen._CHECKOUT_ALGORITHM: Final[str]#

‘base64+ed25519’

abr_sdk.keygen._TIMEOUT_S: Final[int]#

30

abr_sdk.keygen._RESUMABLE_VALIDATION_CODES: Final[frozenset[str]]#

‘frozenset(…)’

class abr_sdk.keygen.Failure(*args, **kwds)#

Bases: enum.Enum

Coarse activation-failure category, mapped to a CLI exit code by the caller.

Initialization

NETWORK#

‘network’

LICENSE#

‘license’

PROTOCOL#

‘protocol’

exception abr_sdk.keygen.ActivationError(kind: abr_sdk.keygen.Failure, message: str)#

Bases: abr_sdk.exceptions.AbrSdkError

An activation step failed.

Carries a coarse :class:Failure kind for the CLI exit code and a human-readable message for stderr; the specific Keygen reason lives in the message, not in a numeric code.

Initialization

Initialize self. See help(type(self)) for accurate signature.

abr_sdk.keygen.unwrap_machine_file_pem(pem_text: str) bytes#

Strip the PEM machine-file markers and base64-decode the body.

Raises ValueError if the markers are missing or the body is not base64.

class abr_sdk.keygen._Response#

One HTTP response: status code and parsed JSON object (None if not an object).

status: int#

None

body: dict[str, Any] | None#

None

abr_sdk.keygen._parse_json_object(raw: bytes) dict[str, Any] | None#

Parse bytes as a JSON object; return None for non-objects or parse errors.

abr_sdk.keygen._first_error_detail(body: collections.abc.Mapping[str, Any] | None) str#

Return a short reason from the first JSON:API error object, or ‘’.

abr_sdk.keygen._resource_id(body: collections.abc.Mapping[str, Any] | None) str | None#

Return data.id from a JSON:API single-resource body, if present.

abr_sdk.keygen._match_machine_id(rows: object, fingerprint: str) str | None#

Return the id of the machine in rows matching fingerprint, or None.

class abr_sdk.keygen.KeygenClient(key: str, *, env: str = 'production', api_base: str = DEFAULT_API_BASE, timeout_s: int = _TIMEOUT_S)#

Customer-facing Keygen client bound to one license key and environment.

Both activation calls share that context, so it is held once here rather than threaded through every method.

Initialization

activate(fingerprint: str) str#

Run the full activation flow and return the signed machine file envelope.

The returned string is the decoded {enc, sig, alg} JSON to hand to the SDK; this client does not verify it. Raises :class:ActivationError on any network, license, or protocol failure.

validate_license_key(fingerprint: str = '') str#

Validate the license key and return its license id.

Request POST /licenses/actions/validate-key {"meta": {"key": <key>, "scope": {"fingerprint": <fp>}}} (the scope is included only when fingerprint is non-empty.) Response 200 {"meta": {"valid": <bool>, "code": <str>}, "data": {"id": <license id>}}

Keygen returns 200 even for an invalid key; the verdict is meta.valid. A False verdict whose meta.code means “device not registered yet” (:data:_RESUMABLE_VALIDATION_CODES) is allowed through so the caller can register the machine.

get_or_create_machine(license_id: str, fingerprint: str) str#

Return the machine id for fingerprint, registering it if absent.

Request GET /licenses//machines Response 200 {"data": [{"id": <machine id>, "attributes":     {"fingerprint": <fp>}}, ...]}

The license’s machines are listed and matched on fingerprint here (rather than via Keygen’s filter[fingerprint], which is not reliably applied on this endpoint). A match means this device is already registered, so re-activation reuses the same machine and returns the same license.

checkout_machine_file(machine_id: str) str#

Check out and return the signed machine file envelope for machine_id.

Request POST /machines//actions/check-out?algorithm=base64+ed25519 Response 200 {"data": {"attributes": {"certificate":     "-----BEGIN MACHINE FILE-----\n<base64>\n-----END MACHINE FILE-----"}}}

The certificate’s base64 body decodes to the {enc, sig, alg} envelope JSON, which is returned verbatim. No ttl is requested; the license’s own expiry is already baked into the payload by the Keygen policy.

_create_machine(license_id: str, fingerprint: str) str#

Register a machine for fingerprint and return its id.

Request POST /machines {"data": {"type": "machines", "attributes": {"fingerprint": <fp>,     "name": <fp>}, "relationships": {"license": {"data": {"type":     "licenses", "id": <license id>}}}}}

_url(path: str, query: collections.abc.Mapping[str, str] | None = None) str#
_headers() dict[str, str]#
_request(method: str, path: str, *, query: collections.abc.Mapping[str, str] | None = None, payload: collections.abc.Mapping[str, Any] | None = None) abr_sdk.keygen._Response#

Send one request, retrying once on a connection error or HTTP 5xx.

_require_object(resp: abr_sdk.keygen._Response, step: str) dict[str, Any]#

Return resp.body, or raise PROTOCOL if it was not a JSON object.

_http_error(resp: abr_sdk.keygen._Response, step: str) abr_sdk.keygen.ActivationError#

Build an :class:ActivationError for a non-success HTTP status.