diff --git a/Cargo.lock b/Cargo.lock index d8bdf80..208a71f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -511,7 +511,7 @@ dependencies = [ [[package]] name = "keysat-licensing-client" -version = "0.2.0" +version = "0.3.0" dependencies = [ "data-encoding", "ed25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index cff7d48..668d942 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "keysat-licensing-client" -version = "0.2.0" +version = "0.3.0" edition = "2021" rust-version = "1.75" description = "Client library for Keysat. Verifies signed license keys offline and wraps the HTTP API for purchase and revocation checks." diff --git a/src/lib.rs b/src/lib.rs index c535004..70d56b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ pub use verify::{Verifier, VerifyOk}; #[cfg(feature = "online")] pub use online::{ - Client, MachineResponse, PollResponse, PublicPoliciesProduct, PublicPoliciesResponse, - PublicPolicy, PurchaseSession, StartPurchaseOptions, ValidateRequest, ValidateResponse, + Client, EntitlementDef, MachineResponse, PollResponse, PublicPoliciesProduct, + PublicPoliciesResponse, PublicPolicy, PurchaseSession, StartPurchaseOptions, + ValidateRequest, ValidateResponse, }; diff --git a/src/online.rs b/src/online.rs index 6ad9ae1..94d760b 100644 --- a/src/online.rs +++ b/src/online.rs @@ -427,6 +427,24 @@ fn default_one() -> i64 { 1 } +/// One entry in a product's entitlements catalog. Operator declares +/// the closed list once per product; policies pick from this list. +/// Use [`EntitlementDef::name`] as the human-readable label when +/// rendering an in-app tier picker (e.g. "AI summaries" instead of +/// the raw `ai_summaries` slug). +#[derive(Debug, Clone, Deserialize)] +pub struct EntitlementDef { + /// Stable identifier — what the SDK's `has_entitlement(slug)` + /// checks against and what gets baked into the signed payload. + pub slug: String, + /// Operator-set display label. Falls back to slug if empty. + #[serde(default)] + pub name: String, + /// Optional one-sentence tooltip. Empty when unset. + #[serde(default)] + pub description: String, +} + /// Product-level fields included in the public-policies response. #[derive(Debug, Clone, Deserialize)] pub struct PublicPoliciesProduct { @@ -440,6 +458,12 @@ pub struct PublicPoliciesProduct { /// Product's base price in sats. Tiers may override via /// [`PublicPolicy::price_sats`]. pub base_price_sats: i64, + /// Closed list of entitlements this product offers, with display + /// names + descriptions for buyer-facing rendering. Empty vec + /// when the operator hasn't defined a catalog (legacy "free-text" + /// mode). + #[serde(default)] + pub entitlements_catalog: Vec, } /// Response from `/v1/products//policies`.