Guia de Integração QuantumLock em Produtos SoftQuantus
Visão Geral
Este guia explica como integrar o sistema de licenciamento QuantumLock em qualquer produto SoftQuantus (QCOS, SynapseX, etc.).
Fluxo Completo
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────────────┐
│ ADMIN │───▶│ QUANTUMLOCK API │───▶│ BANCO DE DADOS PORTAL │
│ Cria Licença │ │ Gera Assinatura │ │ Armazena license_key │
└─────────────────┘ └──────────────────┘ └─────────────────────────────┘
│
┌─────────────────────────────┘
▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────────────┐
│ CLIENTE │◀───│ PORTAL │ │ ARQUIVO .lic │
│ Recebe Licença │ │ Download/Copia │───▶│ ou license_key │
└─────────────────┘ └──────────────────┘ └─────────────────────────────┘
│
┌─────────────────────────────┘
▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────────────┐
│ PRODUTO │ │ quantumlock-sdk │ │ VALIDAÇÃO │
│ SDK/CLI/API │───▶│ LicenseValidator│───▶│ • Offline (assinatura) │
│ │ │ │ │ • Online (API - opcional) │
└─────────────────┘ └──────────────────┘ └─────────────────────────────┘
1. Instalação do SDK
pip install quantumlock-sdk
Ou via requirements.txt:
quantumlock-sdk>=2.0.0
2. Integração em SDK Python
Arquivo: your_product/licensing.py
"""
Módulo de Licenciamento do Produto
"""
import os
from pathlib import Path
from typing import Optional
from quantumlock import LicenseValidator, LicenseError, FeatureNotLicensed
class ProductLicense:
"""Gerenciador de licença do produto."""
# Caminhos padrão para procurar licença
DEFAULT_PATHS = [
Path.home() / ".your_product" / "license.lic",
Path("/etc/your_product/license.lic"),
Path("./license.lic"),
]
def __init__(
self,
license_file: Optional[str] = None,
license_key: Optional[str] = None,
online_validation: bool = True,
):
"""
Inicializa o gerenciador de licença.
Args:
license_file: Caminho para arquivo .lic
license_key: License key (alternativa ao arquivo)
online_validation: Validar online quando possível
"""
self.validator = LicenseValidator(
online_validation=online_validation,
api_url="https://quantumlock.softquantus.com",
)
self._license_file = license_file
self._license_key = license_key
self._validated = False
def find_license(self) -> Optional[Path]:
"""Procura arquivo de licença nos caminhos padrão."""
# 1. Verificar variável de ambiente
env_path = os.getenv("YOUR_PRODUCT_LICENSE")
if env_path and Path(env_path).exists():
return Path(env_path)
# 2. Verificar parâmetro
if self._license_file and Path(self._license_file).exists():
return Path(self._license_file)
# 3. Procurar nos caminhos padrão
for path in self.DEFAULT_PATHS:
if path.exists():
return path
return None
def validate(self) -> bool:
"""
Valida a licença.
Returns:
True se válida
Raises:
LicenseError: Se licença inválida ou não encontrada
"""
license_path = self.find_license()
if not license_path:
raise LicenseError(
"Licença não encontrada. Configure via:\n"
" 1. Variável de ambiente: YOUR_PRODUCT_LICENSE=/path/to/license.lic\n"
" 2. Arquivo em: ~/.your_product/license.lic\n"
" 3. Parâmetro: ProductLicense(license_file='/path/to/license.lic')\n"
"\nObtenha sua licença em: https://portal.softquantus.com"
)
self._validated = self.validator.validate(str(license_path))
return self._validated
def require_valid(self):
"""Exige licença válida ou levanta exceção."""
if not self._validated:
if not self.validate():
raise LicenseError("Licença inválida ou expirada")
def has_feature(self, feature: str) -> bool:
"""Verifica se feature está licenciada."""
self.require_valid()
return self.validator.has_feature(feature)
def require_feature(self, feature: str):
"""Decorator para exigir feature específica."""
return self.validator.require_feature(feature)
@property
def info(self) -> dict:
"""Retorna informações da licença."""
if not self._validated:
try:
self.validate()
except LicenseError:
return {"valid": False, "error": "No license"}
return self.validator.get_license_info()
# Instância global (opcional)
_license: Optional[ProductLicense] = None
def get_license() -> ProductLicense:
"""Retorna instância global da licença."""
global _license
if _license is None:
_license = ProductLicense()
return _license
def require_license(func):
"""Decorator para exigir licença válida."""
from functools import wraps
@wraps(func)
def wrapper(*args, **kwargs):
get_license().require_valid()
return func(*args, **kwargs)
return wrapper
def require_feature(feature: str):
"""Decorator para exigir feature específica."""
def decorator(func):
from functools import wraps
@wraps(func)
def wrapper(*args, **kwargs):
lic = get_license()
if not lic.has_feature(feature):
raise FeatureNotLicensed(
f"Feature '{feature}' não está licenciada. "
f"Upgrade em: https://portal.softquantus.com"
)
return func(*args, **kwargs)
return wrapper
return decorator
Uso no código do produto:
# your_product/client.py
from .licensing import require_license, require_feature, get_license
class YourProductClient:
"""Cliente principal do produto."""
def __init__(self, license_file: str = None):
# Inicializa e valida licença
from .licensing import ProductLicense
self._license = ProductLicense(license_file=license_file)
self._license.validate() # Levanta exceção se inválida
@require_license
def basic_operation(self):
"""Operação básica - requer licença válida."""
return "result"
@require_feature("premium")
def premium_operation(self):
"""Operação premium - requer feature 'premium'."""
return "premium result"
@require_feature("autopilot")
def autopilot_optimize(self, circuit):
"""Otimização com Autopilot - requer feature 'autopilot'."""
return self._run_autopilot(circuit)
@property
def license_info(self) -> dict:
"""Informações da licença atual."""
return self._license.info
3. Integração em CLI
Arquivo: your_product/cli/main.py
"""
CLI do Produto com Licenciamento
"""
import typer
import sys
from pathlib import Path
from typing import Optional
app = typer.Typer(help="Your Product CLI")
# Variável global para licença
LICENSE_KEY_ENV = "YOUR_PRODUCT_LICENSE_KEY"
LICENSE_FILE_ENV = "YOUR_PRODUCT_LICENSE"
def get_license_validator():
"""Obtém validador de licença."""
from quantumlock import LicenseValidator
return LicenseValidator(
online_validation=True,
api_url="https://quantumlock.softquantus.com"
)
def validate_license_or_exit(license_file: Optional[str] = None):
"""Valida licença ou encerra com erro."""
import os
# Procurar licença
paths_to_check = []
if license_file:
paths_to_check.append(Path(license_file))
if os.getenv(LICENSE_FILE_ENV):
paths_to_check.append(Path(os.getenv(LICENSE_FILE_ENV)))
paths_to_check.extend([
Path.home() / ".your_product" / "license.lic",
Path("/etc/your_product/license.lic"),
Path("./license.lic"),
])
validator = get_license_validator()
for path in paths_to_check:
if path.exists():
try:
if validator.validate(str(path)):
return validator
except Exception:
continue
typer.echo("❌ Licença não encontrada ou inválida!", err=True)
typer.echo("\nPara ativar:", err=True)
typer.echo(" 1. your-product activate --license-key 'SUA-CHAVE'", err=True)
typer.echo(" 2. Ou configure: export YOUR_PRODUCT_LICENSE=/path/to/license.lic", err=True)
typer.echo("\nObtenha sua licença em: https://portal.softquantus.com", err=True)
raise typer.Exit(1)
@app.command()
def activate(
license_key: str = typer.Option(None, "--license-key", "-k", help="License key"),
license_file: str = typer.Option(None, "--license-file", "-f", help="License file path"),
):
"""Ativa o produto com uma licença."""
import os
import json
import base64
import requests
if not license_key and not license_file:
typer.echo("❌ Forneça --license-key ou --license-file", err=True)
raise typer.Exit(1)
if license_file:
# Copiar arquivo para diretório padrão
license_path = Path(license_file)
if not license_path.exists():
typer.echo(f"❌ Arquivo não encontrado: {license_file}", err=True)
raise typer.Exit(1)
dest_dir = Path.home() / ".your_product"
dest_dir.mkdir(parents=True, exist_ok=True)
dest_file = dest_dir / "license.lic"
import shutil
shutil.copy(license_path, dest_file)
typer.echo(f"✅ Licença instalada em: {dest_file}")
elif license_key:
# Validar license_key via API e salvar
typer.echo(f"🔍 Validando licença...")
try:
response = requests.post(
"https://quantumlock.softquantus.com/api/v1/licenses/validate",
json={
"license_key": license_key,
"end_customer_id": os.getenv("USER", "cli-user"),
},
timeout=10,
)
if response.status_code == 200 and response.json().get("valid"):
# Salvar licença
dest_dir = Path.home() / ".your_product"
dest_dir.mkdir(parents=True, exist_ok=True)
# Criar arquivo .lic
license_data = {
"license_key": license_key,
"quantum_signature": "validated-via-api",
"valid_until": response.json().get("expires_at", "2099-12-31"),
"features": response.json().get("features", []),
}
license_content = base64.b64encode(
json.dumps(license_data).encode()
).decode()
dest_file = dest_dir / "license.lic"
dest_file.write_text(license_content)
typer.echo(f"✅ Licença ativada com sucesso!")
typer.echo(f" Arquivo: {dest_file}")
typer.echo(f" Expira: {license_data['valid_until']}")
typer.echo(f" Features: {', '.join(license_data['features'])}")
else:
typer.echo("❌ Licença inválida ou expirada", err=True)
raise typer.Exit(1)
except requests.RequestException as e:
typer.echo(f"❌ Erro ao validar: {e}", err=True)
raise typer.Exit(1)
@app.command()
def status():
"""Mostra status da licença atual."""
validator = validate_license_or_exit()
info = validator.get_license_info()
typer.echo("📋 Status da Licença:")
typer.echo(f" ✅ Válida: {info['valid']}")
typer.echo(f" 🔑 Key: {info['license_key'][:20]}...")
typer.echo(f" 📅 Expira: {info['valid_until']}")
typer.echo(f" 🔮 Quantum Verified: {info['quantum_verified']}")
typer.echo(f" 📦 Features: {', '.join(info['features'])}")
@app.command()
def run(
script: str = typer.Argument(..., help="Script to run"),
license_file: str = typer.Option(None, "--license", "-l", help="License file"),
):
"""Executa um script (requer licença válida)."""
validator = validate_license_or_exit(license_file)
typer.echo(f"🚀 Executando: {script}")
# ... sua lógica de execução
@app.command()
def optimize(
script: str = typer.Argument(..., help="Script to optimize"),
):
"""Otimiza um script (requer feature 'autopilot')."""
validator = validate_license_or_exit()
if not validator.has_feature("autopilot"):
typer.echo("❌ Feature 'autopilot' não está licenciada!", err=True)
typer.echo(" Upgrade em: https://portal.softquantus.com", err=True)
raise typer.Exit(1)
typer.echo(f"🔮 Otimizando com Autopilot: {script}")
# ... sua lógica de otimização
if __name__ == "__main__":
app()
4. Integração em API (FastAPI)
Arquivo: your_product/api/deps.py
"""
Dependências da API incluindo validação de licença.
"""
from fastapi import Header, HTTPException, Depends
from typing import Optional
import os
from quantumlock import LicenseValidator, LicenseError
# Cache de validadores
_validator_cache = {}
async def get_license_validator(
x_license_key: Optional[str] = Header(None, alias="X-License-Key"),
x_license_file: Optional[str] = Header(None, alias="X-License-File"),
) -> LicenseValidator:
"""
Dependency para validar licença em cada request.
Headers aceitos:
X-License-Key: QCOS-1234-...
X-License-File: /path/to/license.lic
"""
validator = LicenseValidator(
online_validation=True,
api_url="https://quantumlock.softquantus.com",
)
# Tentar validar por arquivo
if x_license_file and os.path.exists(x_license_file):
try:
if validator.validate(x_license_file):
return validator
except LicenseError as e:
raise HTTPException(status_code=403, detail=str(e))
# Tentar validar por key via API
if x_license_key:
import httpx
try:
async with httpx.AsyncClient() as client:
response = await client.post(
"https://quantumlock.softquantus.com/api/v1/licenses/validate",
json={
"license_key": x_license_key,
"end_customer_id": "api-client",
},
timeout=5.0,
)
if response.status_code == 200 and response.json().get("valid"):
# Criar validator fake com dados da resposta
validator._license_data = response.json()
validator._is_valid = True
return validator
except Exception:
pass
raise HTTPException(
status_code=401,
detail="Licença não fornecida ou inválida. "
"Inclua header X-License-Key ou X-License-File."
)
def require_feature(feature: str):
"""Dependency factory para exigir feature específica."""
async def check_feature(
validator: LicenseValidator = Depends(get_license_validator)
) -> LicenseValidator:
if not validator.has_feature(feature):
raise HTTPException(
status_code=403,
detail=f"Feature '{feature}' não licenciada. "
f"Upgrade em: https://portal.softquantus.com"
)
return validator
return check_feature
Arquivo: your_product/api/routes.py
"""
Rotas da API com proteção de licença.
"""
from fastapi import APIRouter, Depends
from .deps import get_license_validator, require_feature
from quantumlock import LicenseValidator
router = APIRouter()
@router.get("/status")
async def get_status(
validator: LicenseValidator = Depends(get_license_validator)
):
"""Status da licença (requer licença válida)."""
return {
"status": "active",
"license": validator.get_license_info()
}
@router.post("/run")
async def run_job(
job: dict,
validator: LicenseValidator = Depends(get_license_validator)
):
"""Executa job (requer licença válida)."""
# Verificar limite de qubits se definido
info = validator.get_license_info()
max_qubits = info.get("features", [])
# ... lógica de execução
return {"result": "success"}
@router.post("/optimize")
async def optimize_circuit(
circuit: dict,
validator: LicenseValidator = Depends(require_feature("autopilot"))
):
"""Otimiza circuito (requer feature 'autopilot')."""
# ... lógica de otimização
return {"optimized": True}
@router.post("/distributed")
async def distributed_run(
job: dict,
validator: LicenseValidator = Depends(require_feature("distributed"))
):
"""Execução distribuída (requer feature 'distributed')."""
# ... lógica distribuída
return {"nodes": 4, "result": "success"}
5. Variáveis de Ambiente Recomendadas
# Arquivo de licença
YOUR_PRODUCT_LICENSE=/path/to/license.lic
# Ou license key diretamente
YOUR_PRODUCT_LICENSE_KEY=QCOS-1234-...
# Para validação online (opcional)
QUANTUMLOCK_API_URL=https://quantumlock.softquantus.com
6. Features Padrão por Tipo de Licença
| Tipo | Features Incluídas |
|---|---|
starter | core, optimization |
professional | core, optimization, cloud |
enterprise | core, optimization, cloud, distributed, autopilot |
7. Publicação do Produto
PyPI (SDK)
# Adicionar dependência no pyproject.toml
[project]
dependencies = [
"quantumlock-sdk>=2.0.0",
# ... outras deps
]
# Build e publish
python -m build
twine upload dist/*
Docker (API)
FROM python:3.11-slim
# Instalar dependências incluindo quantumlock-sdk
COPY requirements.txt .
RUN pip install -r requirements.txt
# Copiar código
COPY . /app
WORKDIR /app
# Variável para licença (montada em runtime)
ENV YOUR_PRODUCT_LICENSE=/etc/license/license.lic
CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000"]
GitHub Releases (CLI)
# Build binário com Nuitka
nuitka --onefile --output-dir=dist your_product/cli/main.py
# Upload para GitHub Releases
gh release create v1.0.0 dist/your_product --title "v1.0.0"
8. Exemplo de Uso pelo Cliente
# Instalar
pip install your-product
# Ativar
your-product activate --license-key "QCOS-1234-..."
# Usar SDK
from your_product import Client
client = Client() # Usa licença de ~/.your_product/license.lic
result = client.run(my_job)
# Ou com licença explícita
client = Client(license_file="/custom/path/license.lic")
Suporte
- Portal: https://portal.softquantus.com
- Docs: https://docs.softquantus.com
- Email: support@softquantus.com