Skip to main content

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 with statements
  • 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()
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

  1. Acquisition - Heartbeat daemon starts immediately after successful license acquisition
  2. Interval - Heartbeat sent every 2 minutes (default, configurable)
  3. TTL - Each heartbeat extends session TTL by 6 minutes
  4. Failure Handling - Exponential backoff on failures (1s, 2s, 4s, 8s, max 30s)
  5. Daemon Thread - Runs in background, doesn't block program exit
  6. 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 events
  • WARNING - Heartbeat failures, retries
  • ERROR - 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)dict
  • release_license(raise_on_error=False)bool
  • get_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:

  1. Linux: /etc/machine-id
  2. macOS: IOPlatformUUID
  3. Windows: MachineGuid registry key
  4. Fallback: MAC address hash

Exceptions

LicenseSDKError

Base exception for all SDK errors.

LicenseAcquisitionError

Failed to acquire license seat.

Attributes:

  • status_code - HTTP status code
  • response_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 timestamp
  • expired_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

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)