stylobridge
1.0.0Air-gapped MCP document converter — Markdown/PDF to branded DOCX with audit logging
License Sources
| Source | License | Class |
|---|---|---|
Licensie (detected) | Pending | - |
PyPI (reported) | Not reported | - |
License detection is still in progress for this version.
Loading dependencies…
License File
"""JWT license validation for StyloBridge Enterprise.
Uses RS256 asymmetric signing with audience claim to prevent
cross-app token replay attacks.
"""
import os
from pathlib import Path
import jwt
from src.schemas.license_claims import LicenseClaims
class LicenseError(Exception):
"""Raised when license validation fails."""
# Load public key once at module level
_PUBLIC_KEY: str | None = None
def _get_public_key() -> str:
"""Load and cache the RSA public key."""
global _PUBLIC_KEY
if _PUBLIC_KEY is not None:
return _PUBLIC_KEY
# Try environment variable first, then file
env_key = os.getenv("STYLOBRIDGE_PUBLIC_KEY")
if env_key:
_PUBLIC_KEY = env_key
return _PUBLIC_KEY
key_path = Path(__file__).parent / "public_key.pem"
if key_path.exists():
if key_path.is_symlink():
raise LicenseError("Public key file must not be a symlink")
_PUBLIC_KEY = key_path.read_text()
return _PUBLIC_KEY
raise LicenseError("No public key found. Set STYLOBRIDGE_PUBLIC_KEY or place src/public_key.pem")
def validate_license(token: str) -> dict:
"""Validate a StyloBridge JWT license token.
Enforcement logic:
1. Validate RS256 signature (rejects tampered/forged tokens)
2. Check expiration date (rejects expired licenses)
3. Return claims including 'tier' for feature gating
Tier-based access (enforced in main.py):
- "standard" → convert_to_docx (no templates, no audit log)
- "enterprise" → all tools + corporate templates + audit logging
The 'seats' claim is metadata for billing/invoicing —
not enforced at runtime.
Args:
token: The JWT token string.
Returns:
Decoded claims as a dict.
Raises:
LicenseError: If the token is invalid, expired, or has wrong claims.
"""
if not token:
raise LicenseError("No license token provided")
public_key = _get_public_key()
# Step 1: Validate signature (RS256)
# Step 2: Check expiration (built into jwt.decode)
try:
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"],
issuer="stylobridge",
audience="stylobridge",
)
except jwt.ExpiredSignatureError:
raise LicenseError("License has expired")
except jwt.InvalidIssuerError:
raise LicenseError("Invalid license issuer")
except jwt.InvalidAudienceError:
raise LicenseError("Invalid license audience (possible cross-app token replay)")
except jwt.InvalidSignatureError:
raise LicenseError("Invalid license signature (token may be tampered)")
except jwt.DecodeError:
raise LicenseError("Failed to decode license token")
# Step 3: Validate structure (including tier)
try:
LicenseClaims(**payload)
except Exception:
raise LicenseError("License token has invalid or missing claims")
return payload
Versions
1 version| Version | License | Published | Status |
|---|---|---|---|
| 1.0.0 Latest Viewing | - | Mar 21, 2026 | Pending |