Táillí License Client for Python
A friendly Python client for the Táillí License Manager. Manage seats and API keys for offline/air-gapped deployments with minimal code.
Installation
pip install tailli-license
Or install from source:
cd license-manager/clients/python
pip install -e .
Quick Start
from tailli_license import LicenseClient
# Connect to your License Manager
client = LicenseClient(
base_url="http://localhost:8787",
product_id="my-product"
)
# Use a seat with automatic cleanup
with client.seat() as seat:
print(f"Allocated seat: {seat.seat_id}")
# Do your licensed work here
run_my_application()
# Seat automatically released when done
Features
- Simple context manager - Seats are automatically released, even on exceptions
- Auto-refresh - Tokens are refreshed in the background, no manual work needed
- Zero dependencies - Uses only Python standard library
- Type hints - Full type annotations for IDE support
- Friendly errors - Clear exceptions for common issues (seat limit, expired license)
Usage Examples
Check License Status
from tailli_license import LicenseClient
client = LicenseClient("http://localhost:8787", product_id="video-processor")
status = client.status()
print(f"Product: {status['productId']}")
print(f"Seats: {status['seatsInUse']}/{status['seats']} in use")
print(f"Expires: {status['expiry']}")
print(f"Features: {', '.join(status['features'])}")
Use a Seat (Recommended)
The context manager is the cleanest way to use seats:
from tailli_license import LicenseClient, SeatLimitError, LicenseExpiredError
client = LicenseClient("http://localhost:8787", product_id="video-processor")
try:
with client.seat() as seat:
print(f"Got seat {seat.seat_id}")
# Your licensed code here
process_videos()
except SeatLimitError:
print("All seats are in use. Please try again later.")
except LicenseExpiredError:
print("Your license has expired. Please contact support.")
Add Context for Auditing
Track who's using seats for compliance and debugging:
with client.seat(context={"user": "alice", "workstation": "lab-pc-42"}) as seat:
run_analysis()
Manual Seat Management
For more control, manage seats directly:
# Allocate a seat
seat = client.allocate(context={"user": "bob"})
try:
# Do work...
for batch in data_batches:
process(batch)
finally:
# Always release when done
seat.release()
Disable Auto-Refresh
For short operations, you might not need background refresh:
with client.seat(auto_refresh=False) as seat:
quick_operation() # Done in < 1 minute
Check Feature Entitlements
See what features your license enables:
entitlements = client.get_entitlements()
if "ai-enhancement" in entitlements["features"]:
enable_ai_features()
if "batch-processing" in entitlements["features"]:
enable_batch_mode()
print(f"API keys: {entitlements['keysIssued']}/{entitlements['maxKeys']} used")
Verify User API Keys
Validate JWTs issued by the License Manager:
try:
payload = client.verify_key(user_provided_token)
print(f"Valid token for product: {payload['productId']}")
print(f"Features: {payload['features']}")
except LicenseError:
print("Invalid or expired token")
Error Handling
The client provides specific exceptions for common scenarios:
from tailli_license import (
LicenseClient,
LicenseError, # Base class for all errors
SeatLimitError, # All seats are in use
LicenseExpiredError, # License has expired
ConnectionError, # Can't reach License Manager
)
client = LicenseClient("http://localhost:8787", product_id="my-product")
try:
with client.seat() as seat:
do_work()
except SeatLimitError:
# All seats taken - maybe queue the request or notify user
queue_for_later()
except LicenseExpiredError:
# License expired - notify admin
notify_admin("License expired!")
except ConnectionError:
# Can't reach LM - maybe it's down or network issue
use_cached_state_or_fail_gracefully()
except LicenseError as e:
# Other license-related errors
log_error(f"License error: {e}")
Configuration
Client Options
client = LicenseClient(
base_url="http://localhost:8787", # License Manager URL
product_id="my-product", # Your product ID
timeout=30.0, # Request timeout in seconds
)
Seat Options
with client.seat(
context={"user": "alice"}, # Metadata for auditing
auto_refresh=True, # Keep token fresh (default: True)
) as seat:
pass
Logging
Enable debug logging to see what's happening:
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("tailli_license")
logger.setLevel(logging.DEBUG)
Thread Safety
The LicenseClient is thread-safe. You can share one client across threads:
from concurrent.futures import ThreadPoolExecutor
client = LicenseClient("http://localhost:8787", product_id="my-product")
def worker(task_id):
with client.seat(context={"task": task_id}) as seat:
process_task(task_id)
with ThreadPoolExecutor(max_workers=4) as executor:
executor.map(worker, range(10))
Each seat() call gets its own seat allocation.
API Reference
LicenseClient
| Method | Description |
|---|---|
status() |
Get license status (seats, expiry, features) |
allocate(context, auto_refresh) |
Allocate a seat, returns Seat object |
seat(context, auto_refresh) |
Context manager for seat allocation |
verify_key(token) |
Verify a user API key (JWT) |
get_entitlements() |
Get feature entitlements |
Seat
| Method/Attribute | Description |
|---|---|
seat_id |
Unique identifier for this seat |
token |
Current authentication token |
expires_at |
Token expiration time |
release() |
Release the seat back to the pool |
refresh() |
Manually refresh the token |
License
MIT License - see the main Táillí repository for details.
Metadata
Release files for tailli-license 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| tailli_license-0.1.0.tar.gz | 11.7 kB | With provenance | Details for tailli_license-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| tailli_license-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for tailli_license-0.1.0-py3-none-any.whl |
Total release size: 20.0 kB
Release files / tailli_license-0.1.0.tar.gz
| Download URL | tailli_license-0.1.0.tar.gz |
|---|---|
| Size | 11.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
19d57bad04f23048ce801d5d5e1b25f7aed9332938d6f68b2d708822c89380bd
|
|
BLAKE2b-256 checksum How to use checksums |
71e740bf46509a1722089a606a6cfaaac56d7b3b569202a9270eaed5bf6f0133
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 5, 2026.
Transparency logRelease files / tailli_license-0.1.0-py3-none-any.whl
| Download URL | tailli_license-0.1.0-py3-none-any.whl |
|---|---|
| Size | 8.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
da60a37702b26a972a3819dada6494cbb4c107beb5581ae7acedd52de1393022
|
|
BLAKE2b-256 checksum How to use checksums |
c6c668b4b58739bb10831eddb0dbbd4a346048294ecf990b9ee357c0aef33ca8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 5, 2026.
Transparency logSecurity for tailli-license 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for tailli_license-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 5, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.