"""License module — registration, activation, expiration, feature gating. Public API the rest of the app uses: - :func:`get_manager` — singleton :class:`LicenseManager` instance. - :func:`current_state` — quick snapshot for status badges / tests. - :func:`require_feature` — raise :class:`LicenseError` if a feature isn't unlocked by the active license. - :class:`License`, :class:`Tier`, :class:`FeatureFlag` — schema. - :class:`LicenseError` and subclasses — typed failures the UI can branch on (not yet activated vs. expired vs. tampered). The license model is: 1. The seller (creator) runs ``scripts/generate_license.py`` to mint a signed **license blob** keyed to a buyer's name + email. 2. The buyer pastes the blob into the activation page on first launch. 3. The app verifies the HMAC signature locally (no internet), then writes a canonical ``~/.datatools/license.json`` and the app unlocks. The signature is HMAC-SHA256 with a build-time secret. Combined with the 30-day refund policy, this is honor-system DRM — see ``docs/DECISIONS.md`` for the trade-off discussion. """ from __future__ import annotations from .errors import ( ExpiredLicenseError, InvalidLicenseError, LicenseError, NotActivatedError, UnsupportedFeatureError, ) from .features import FEATURES_BY_TIER, all_features_for_tier from .manager import ( LicenseManager, ProductionBuildError, assert_production_safe, current_state, get_manager, require_feature, ) from .schema import FeatureFlag, License, Tier __all__ = [ # Manager "LicenseManager", "ProductionBuildError", "assert_production_safe", "current_state", "get_manager", "require_feature", # Schema "FeatureFlag", "License", "Tier", # Feature registry "FEATURES_BY_TIER", "all_features_for_tier", # Errors "LicenseError", "NotActivatedError", "ExpiredLicenseError", "InvalidLicenseError", "UnsupportedFeatureError", ]