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:validate-key - confirm the key is valid; learn the license id.
machine - find this fingerprint’s machine, or register a new one.
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#
Coarse activation-failure category, mapped to a CLI exit code by the caller. |
|
One HTTP response: status code and parsed JSON object (None if not an object). |
|
Customer-facing Keygen client bound to one license key and environment. |
Functions#
Strip the PEM machine-file markers and base64-decode the body. |
|
Parse bytes as a JSON object; return None for non-objects or parse errors. |
|
Return a short reason from the first JSON:API error object, or ‘’. |
|
Return |
|
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.EnumCoarse 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.AbrSdkErrorAn activation step failed.
Carries a coarse :class:
Failurekind 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.idfrom 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:ActivationErroron 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:
ActivationErrorfor a non-success HTTP status.