Skip to main content

sdkey

Official Python client for SDKey license authentication.

Implements the sealed session protocol: Ed25519-verified handshake, HKDF session keys, and AES-256-GCM envelopes for validate plus client auth (register / login / upgrade). See PROTOCOL.md.

Install

pip install sdkey

Requires Python 3.10+.

Quick start

Embed these values from the SDKey dashboard when you ship your app. app_version must exactly match the application version configured on the server (clientVersion on session init); mismatch returns APP_OUTDATED.

from sdkey import SdkeyClient, SdkeyError

client = SdkeyClient(
    api_base_url="https://api.sdkey.dev",
    app_id="YOUR_APP_ID",
    app_version="1.0.0",
    app_public_key_b64="YOUR_APP_PUBLIC_KEY_BASE64",
)

try:
    # hwid is optional (omit for web clients — server skips HWID checks)
    result = client.validate("SDKY-XXXX-XXXX-XXXX-XXXX", "machine-hwid")
    if result.success:
        print("licensed", result.status, result.expires_at, result.subscription_tier)
        print("message", result.message)
    else:
        print("denied", result.code, result.message)
except SdkeyError as err:
    # Init / transport failures use `error` text from the server when present
    print(err.code, err.message)
    raise

validate, register, login, and upgrade call init() automatically when no session exists and reuse an active session when present. Sessions last ~15 minutes server-side; on SESSION_EXPIRED the client clears local state so the next call re-handshakes.

Hardware ID (desktop)

Use get_hardware_id() on desktop apps to bind a license to the machine. It is opt-in — pass the result explicitly; the client never auto-injects HWID.

from sdkey import SdkeyClient, get_hardware_id

client = SdkeyClient(...)
result = client.validate("SDKY-XXXX-XXXX-XXXX-XXXX", get_hardware_id())

get_hardware_id() reads a stable OS machine identifier (Windows MachineGuid, Linux /etc/machine-id, macOS IOPlatformUUID), then returns the lowercase SHA-256 hex digest. On unsupported platforms or missing IDs it raises SdkeyError with code HWID_UNAVAILABLE — it does not invent a random ID. Omit hwid for web clients.

Client auth (sealed)

Register, login, and upgrade use the same sealed-session wire model as validate. Application binding and version gating come from the crypto session — do not send appId / clientVersion in the auth body.

reg = client.register(
    username="player1",
    password="••••••••",
    license_key="SDKY-XXXX-XXXX-XXXX-XXXX",
    hwid="machine-hwid",  # optional
)
if not reg.success:
    # Sealed failures expose plaintext `message` on ClientAuthResult.error
    print(reg.code, reg.error)
else:
    print(reg.session_token, reg.user, reg.license)

login = client.login(username="player1", password="••••••••")
upgrade = client.upgrade(username="player1", license_key="SDKY-HIGHER-TIER-KEY")

upgrade takes username + license key only (no password). The new key’s subscriptionTier must be strictly greater than the user’s current tier.

Breaking change (0.4.0): plaintext JSON client-auth bodies are rejected when the API has CRYPTO_ENFORCE=true (400 CRYPTO_REQUIRED). Use this SDK (or another sealed client) against production.

Where message vs error appears

Per-app responseMessages may customize many strings. The SDK surfaces whatever the server returns.

Surface Success text field Failure text field
Session init (none) error (raised as SdkeyError.message)
Sealed validate message message
Sealed register / login / upgrade message message (exposed as ClientAuthResult.error)

Example JSON shapes

Init failure (plaintext):

{ "success": false, "error": "Client version outdated", "code": "APP_OUTDATED" }

Sealed validate success (message):

{
  "success": true,
  "code": "OK",
  "message": "validated",
  "status": "active",
  "expiresAt": "2026-01-01T00:00:00.000Z",
  "subscriptionTier": 0,
  "sessionId": "...",
  "timestamp": 1720000001,
  "v": 1
}

Sealed validate failure (still message, not error):

{
  "success": false,
  "code": "HWID_MISMATCH",
  "message": "Hardware ID mismatch",
  "status": null,
  "expiresAt": null,
  "sessionId": "...",
  "timestamp": 1720000001,
  "v": 1
}

Sealed client auth failure (message inside the opened plaintext):

{
  "success": false,
  "code": "TIER_NOT_HIGHER",
  "message": "License tier must be higher than the current tier",
  "sessionId": "...",
  "timestamp": 1720000001,
  "v": 1
}

API

SdkeyClient(options)

Option Type Description
api_base_url str API origin (no trailing slash)
app_id str Application UUID
app_version str Exact app version → sent as clientVersion on session init
app_public_key_b64 str Raw Ed25519 public key (32 bytes), base64
http_post callable Optional HTTP POST override (tests / custom transport)

Methods

  • init() — challenge handshake; verifies the signed hello; derives the AES session key; sends clientVersion
  • validate(license_key, hwid=None) — sealed validate; omits hwid JSON key when not provided; always decrypts then verifies the Ed25519 signature before trusting success
  • register(...) / login(...) / upgrade(...) — sealed POST /api/v1/client/* (same outer envelope + verify order as validate)
  • get_session() / clear_session() — inspect or drop the local session
  • get_hardware_id() — package-level helper; SHA-256 hex of a stable OS machine ID (desktop opt-in)

Errors

Protocol / transport failures raise SdkeyError with a code and message (server error text when the API provides one):

INIT_FAILED · APP_OUTDATED · HELLO_SIGNATURE_INVALID · VALIDATE_RESPONSE_INVALID · RESPONSE_INVALID · RESPONSE_SIGNATURE_INVALID · SESSION_MISMATCH · CLOCK_SKEW · NETWORK · HWID_UNAVAILABLE

License denials (banned, HWID mismatch, etc.) return a normal ValidateResult with success=False — they are not raised. Auth denials return ClientAuthResult(success=False, code=..., error=...) where error is the sealed plaintext message.

This package does not include developer tooling / Bearer (sdk_live_…) management APIs.

Security notes

  • Never ship app private keys in a client.
  • Do not skip signature verification — that is the anti-spoof binding.
  • This package is open source; the SDKey server remains a separate product.

Development

python -m pip install -e ".[dev]"
pytest

License

MIT

Metadata

Release files for sdkey 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sdkey 0.4.0
File Size Uploaded Provenance
sdkey-0.4.0.tar.gz 20.2 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for sdkey 0.4.0
File Interpreter ABI Platform
sdkey-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 35.4 kB

Release files / sdkey-0.4.0.tar.gz

Download URL sdkey-0.4.0.tar.gz
Size 20.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f350890580c82650af55e4d047fc22707f8caa53ad605fa87f1c62af230f9e12
BLAKE2b-256 checksum
How to use checksums
de65022649aa37528d68f5016d316b7d780aa18be0055efbf1ebf969dc711c09
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.

Transparency log

Release files / sdkey-0.4.0-py3-none-any.whl

Download URL sdkey-0.4.0-py3-none-any.whl
Size 15.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b3fb960f5718bf4741f9103986643f2414d589dcb3ae19d7a054bf5b7d0d0a99
BLAKE2b-256 checksum
How to use checksums
59cb26e9f2c03be8bc3f0bf8839f1f0e4c82f17a03c4febb8636cfac0ef22be4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.

Transparency log

Security for sdkey 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for sdkey-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page