CODITECT License SDK - Python Client
Production-ready Python SDK for automated license seat management with heartbeat automation.
Features
- Automatic License Acquisition - Atomic seat allocation with retry logic
- Background Heartbeat Daemon - Automatic TTL refresh every 2 minutes
- Graceful Shutdown - Signal handlers (SIGTERM, SIGINT) and atexit cleanup
- Thread-Safe Operations - Concurrent operation support with locking
- Context Manager Support - Clean resource management with
withstatements - Cross-Platform - Windows, macOS, Linux compatible
- Comprehensive Error Handling - Detailed exceptions with retry strategies
Installation
From Source
cd coditect-cloud-backend
pip install -e .
From PyPI (When Published)
pip install coditect-license-sdk
Quick Start
Basic Usage
from coditect_license_sdk import LicenseClient, get_machine_id
# Initialize client
client = LicenseClient(
api_url="https://api.coditect.com",
api_key="your-api-key"
)
# Acquire license (starts automatic heartbeats)
session = client.acquire_license(
license_key="CODITECT-2025-XXXX-XXXX",
machine_id=get_machine_id()
)
print(f"License acquired: {session['id']}")
print(f"Expires at: {session['expires_at']}")
# Your application code here
# Heartbeats happen automatically every 2 minutes
# Release license when done
client.release_license()
Context Manager (Recommended)
from coditect_license_sdk import LicenseClient, get_machine_id
with LicenseClient(
api_url="https://api.coditect.com",
api_key="your-api-key"
) as client:
session = client.acquire_license(
license_key="CODITECT-2025-XXXX-XXXX",
machine_id=get_machine_id()
)
# Your application code here
# Automatic heartbeats running
# Automatic release on context exit
With Custom Metadata
from coditect_license_sdk import LicenseClient, get_machine_id
client = LicenseClient(
api_url="https://api.coditect.com",
api_key="your-api-key",
heartbeat_interval=180, # Custom interval (3 minutes)
)
session = client.acquire_license(
license_key="CODITECT-2025-XXXX-XXXX",
machine_id=get_machine_id(),
metadata={
"app_version": "1.0.0",
"user_name": "john.doe",
"project_id": "project-123",
}
)
Configuration
LicenseClient Parameters
LicenseClient(
api_url: str, # Base API URL (required)
api_key: str, # JWT authentication token (required)
timeout: int = 30, # Request timeout in seconds
max_retries: int = 3, # Maximum retry attempts
heartbeat_interval: int = 120, # Heartbeat interval in seconds
auto_release_on_exit: bool = True, # Auto-release on program exit
)
Environment Variables (Optional)
export CODITECT_API_URL="https://api.coditect.com"
export CODITECT_API_KEY="your-api-key"
export CODITECT_LICENSE_KEY="CODITECT-2025-XXXX-XXXX"
Advanced Usage
Error Handling
from coditect_license_sdk import (
LicenseClient,
LicenseAcquisitionError,
APIConnectionError,
)
try:
client = LicenseClient(
api_url="https://api.coditect.com",
api_key="your-api-key"
)
session = client.acquire_license(
license_key="CODITECT-2025-XXXX-XXXX"
)
except LicenseAcquisitionError as e:
if e.status_code == 409:
print("All license seats are in use. Please try again later.")
print(f"Max seats: {e.response_data.get('max_seats')}")
print(f"Retry after: {e.response_data.get('retry_after_seconds')}s")
elif e.status_code == 400:
print(f"Validation error: {e.response_data}")
else:
print(f"Acquisition failed: {e}")
except APIConnectionError as e:
print(f"Network error: {e}")
print(f"Original exception: {e.original_exception}")
Manual Session Management
client = LicenseClient(
api_url="https://api.coditect.com",
api_key="your-api-key"
)
# Acquire
session = client.acquire_license(license_key="CODITECT-2025-XXXX-XXXX")
# Check status
if client.is_active():
status = client.get_session_status()
print(f"Session ID: {status['id']}")
print(f"Expires at: {status['expires_at']}")
print(f"Active: {status['is_active']}")
# Release
result = client.release_license(raise_on_error=True)
print(f"Released: {result}")
Custom Machine ID
import hashlib
from coditect_license_sdk import LicenseClient
def custom_machine_id():
"""Generate custom machine identifier."""
# Use your own logic here
unique_id = "my-custom-identifier"
return hashlib.sha256(unique_id.encode()).hexdigest()
client = LicenseClient(
api_url="https://api.coditect.com",
api_key="your-api-key"
)
session = client.acquire_license(
license_key="CODITECT-2025-XXXX-XXXX",
machine_id=custom_machine_id()
)
Disable Auto-Release (For Testing)
client = LicenseClient(
api_url="https://api.coditect.com",
api_key="your-api-key",
auto_release_on_exit=False, # Disable automatic cleanup
)
session = client.acquire_license(license_key="CODITECT-2025-XXXX-XXXX")
# Manual cleanup required
client.release_license()
Heartbeat Behavior
How It Works
- Acquisition - Heartbeat daemon starts immediately after successful license acquisition
- Interval - Heartbeat sent every 2 minutes (default, configurable)
- TTL - Each heartbeat extends session TTL by 6 minutes
- Failure Handling - Exponential backoff on failures (1s, 2s, 4s, 8s, max 30s)
- Daemon Thread - Runs in background, doesn't block program exit
- Graceful Shutdown - Stops automatically on release or program exit
Custom Heartbeat Interval
# Heartbeat every 3 minutes (180 seconds)
client = LicenseClient(
api_url="https://api.coditect.com",
api_key="your-api-key",
heartbeat_interval=180,
)
Important: Keep heartbeat interval < 6 minutes (TTL) to prevent session expiration.
Recommended: 2-3 minutes for production use.
Signal Handling
The SDK automatically handles shutdown signals:
- SIGTERM - Graceful shutdown (e.g.,
kill <pid>) - SIGINT - Keyboard interrupt (Ctrl+C)
- atexit - Normal program exit
All handlers release the license before exiting.
Testing Signal Handlers
# Start your application
python my_app.py &
PID=$!
# Send SIGTERM (should release license gracefully)
kill -TERM $PID
# Or Ctrl+C for SIGINT
python my_app.py
^C # Press Ctrl+C
Testing
Unit Tests
# Run unit tests (no API required)
pytest tests/sdk/test_client.py tests/sdk/test_heartbeat.py -v
Integration Tests
# Set environment variables
export CODITECT_API_URL="https://api.coditect.test"
export CODITECT_API_KEY="your-test-api-key"
export CODITECT_TEST_LICENSE_KEY="CODITECT-2025-TEST-KEY"
# Run integration tests (requires live API)
pytest tests/sdk/test_integration.py -v
Test Coverage
pytest --cov=coditect_license_sdk --cov-report=html tests/sdk/
Logging
Enable debug logging to see SDK internal operations:
import logging
# Configure logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# SDK will now log debug messages
client = LicenseClient(
api_url="https://api.coditect.com",
api_key="your-api-key"
)
Log levels:
DEBUG- Detailed internal operations (heartbeats, state changes)INFO- Acquisition, release, lifecycle eventsWARNING- Heartbeat failures, retriesERROR- Critical errors, exceptions
Troubleshooting
License Acquisition Fails (409 Conflict)
Problem: All license seats are in use.
Solution:
- Wait for seats to become available (6-minute TTL)
- Contact admin to increase seat count
- Check for zombie processes holding seats
Heartbeat Failures
Problem: Network errors or API unavailable.
Solution:
- SDK automatically retries with exponential backoff
- Check network connectivity
- Verify API URL is correct
- Check firewall rules
Session Expired (410 Gone)
Problem: Session expired due to missed heartbeats.
Solution:
- Ensure heartbeat interval < 6 minutes
- Check network stability
- Re-acquire license
Signal Handlers Not Working
Problem: License not released on Ctrl+C.
Solution:
- Verify
auto_release_on_exit=True(default) - Check if signal handlers are being overridden
- Use context manager for guaranteed cleanup
API Reference
Classes
LicenseClient
Main client for license management.
Methods:
acquire_license(license_key, machine_id=None, metadata=None)→dictrelease_license(raise_on_error=False)→boolget_session_status()→Optional[dict]is_active()→bool
HeartbeatDaemon
Background daemon for automatic heartbeats (internal use).
Functions
get_machine_id()
Auto-detect unique hardware identifier.
Returns: str - SHA256 hash of machine ID
Priority:
- Linux:
/etc/machine-id - macOS:
IOPlatformUUID - Windows:
MachineGuidregistry key - Fallback: MAC address hash
Exceptions
LicenseSDKError
Base exception for all SDK errors.
LicenseAcquisitionError
Failed to acquire license seat.
Attributes:
status_code- HTTP status coderesponse_data- API response details
LicenseReleaseError
Failed to release license seat.
HeartbeatError
Heartbeat failed.
APIConnectionError
Network connection failed.
Attributes:
original_exception- Original exception object
SessionExpiredError
Session has expired (410 Gone).
Attributes:
last_heartbeat_at- Last heartbeat timestampexpired_at- Expiration timestamp
Production Checklist
- Use environment variables for API credentials (not hardcoded)
- Enable logging with appropriate level (INFO or WARNING)
- Set heartbeat interval to 2-3 minutes
- Use context manager for automatic cleanup
- Implement error handling for all acquisition attempts
- Test signal handlers (SIGTERM, SIGINT)
- Monitor heartbeat failures in logs
- Set up alerting for license acquisition failures
- Document required firewall rules for API access
- Test license release on abnormal termination
Examples
CLI Application
#!/usr/bin/env python3
import sys
from coditect_license_sdk import LicenseClient, get_machine_id
def main():
client = LicenseClient(
api_url="https://api.coditect.com",
api_key="your-api-key"
)
try:
session = client.acquire_license(
license_key="CODITECT-2025-XXXX-XXXX",
machine_id=get_machine_id()
)
print(f"License acquired: {session['id']}")
print("Application running... (Ctrl+C to exit)")
# Your application logic here
while True:
pass # Do work
except KeyboardInterrupt:
print("\nShutting down...")
client.release_license()
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
Long-Running Service
import logging
import time
from coditect_license_sdk import LicenseClient, get_machine_id
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def run_service():
with LicenseClient(
api_url="https://api.coditect.com",
api_key="your-api-key",
heartbeat_interval=120,
) as client:
session = client.acquire_license(
license_key="CODITECT-2025-XXXX-XXXX",
machine_id=get_machine_id(),
metadata={
"service_name": "data-processor",
"version": "1.0.0",
}
)
logging.info(f"Service started: {session['id']}")
# Service loop
while True:
# Process work
time.sleep(1)
if __name__ == "__main__":
try:
run_service()
except KeyboardInterrupt:
logging.info("Service stopped")
Support
- Documentation: https://docs.coditect.com
- API Reference: https://api.coditect.com/docs
- GitHub: https://github.com/coditect-ai/coditect-cloud-backend
- Email: support@coditect.com
License
Proprietary - AZ1.AI INC
Changelog
v1.0.0 (2025-12-01)
- Initial release
- Automatic license acquisition with retry
- Background heartbeat daemon (every 2 minutes)
- Graceful shutdown (SIGTERM, SIGINT, atexit)
- Thread-safe operations
- Context manager support
- Cross-platform support (Windows, macOS, Linux)
- Comprehensive test suite (>80% coverage)