From f2ea74d7e324c45948dd1046494d573cce4505d6 Mon Sep 17 00:00:00 2001 From: Keysat Date: Sun, 10 May 2026 07:58:59 -0500 Subject: [PATCH] =?UTF-8?q?v0.3.0=20=E2=80=94=20entitlements=20catalog=20i?= =?UTF-8?q?n=20PublicPoliciesResponse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors keysat 0014 + TS/Rust SDK 0.3.0. PublicPoliciesProduct gains entitlements_catalog: list[EntitlementDef] with slug + name + description. SDK consumers' in-app tier pickers can render display names + tooltip descriptions instead of raw slugs. Empty list on legacy products without a catalog. No breaking change. --- pyproject.toml | 2 +- src/keysat_licensing_client/__init__.py | 4 +++- src/keysat_licensing_client/online.py | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6b365a7..34db73b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "keysat-licensing-client" -version = "0.2.0" +version = "0.3.0" description = "Python client for Keysat — a self-hosted Bitcoin-paid software licensing server that runs on Start9. Verifies signed license keys offline and wraps the HTTP API for purchase, redemption, and revocation checks." readme = "README.md" requires-python = ">=3.10" diff --git a/src/keysat_licensing_client/__init__.py b/src/keysat_licensing_client/__init__.py index 523e23c..b5db427 100644 --- a/src/keysat_licensing_client/__init__.py +++ b/src/keysat_licensing_client/__init__.py @@ -67,6 +67,7 @@ try: ValidateResponse, ValidateOptions, StartPurchaseOptions, + EntitlementDef, PublicPolicy, PublicPoliciesProduct, PublicPoliciesResponse, @@ -81,6 +82,7 @@ try: "ValidateResponse", "ValidateOptions", "StartPurchaseOptions", + "EntitlementDef", "PublicPolicy", "PublicPoliciesProduct", "PublicPoliciesResponse", @@ -93,4 +95,4 @@ except ImportError: # httpx not installed — that's fine, online client is optional. pass -__version__ = "0.2.0" +__version__ = "0.3.0" diff --git a/src/keysat_licensing_client/online.py b/src/keysat_licensing_client/online.py index 03b2158..594bd67 100644 --- a/src/keysat_licensing_client/online.py +++ b/src/keysat_licensing_client/online.py @@ -89,12 +89,27 @@ class PublicPolicy: trial_days: int +@dataclass +class EntitlementDef: + """One entry in a product's entitlements catalog (Keysat + migration 0014). Operator declares the closed list once per + product; policies pick from this list. Use ``name`` as the + human-readable label when rendering an in-app tier picker + (e.g. "AI summaries" instead of the raw ``ai_summaries`` slug). + """ + + slug: str + name: str + description: str + + @dataclass class PublicPoliciesProduct: slug: str name: str description: str base_price_sats: int + entitlements_catalog: list[EntitlementDef] @dataclass @@ -277,12 +292,21 @@ class Client: raw = self._get(f"/v1/products/{product_slug}/policies") product = raw.get("product", {}) or {} policies_raw = raw.get("policies") or [] + catalog_raw = product.get("entitlements_catalog") or [] return PublicPoliciesResponse( product=PublicPoliciesProduct( slug=product.get("slug", ""), name=product.get("name", ""), description=product.get("description", "") or "", base_price_sats=int(product.get("base_price_sats", 0)), + entitlements_catalog=[ + EntitlementDef( + slug=c.get("slug", ""), + name=c.get("name", "") or c.get("slug", ""), + description=c.get("description", "") or "", + ) + for c in catalog_raw + ], ), policies=[ PublicPolicy(