A comprehensive Python client library for Calimero Network APIs, built with PyO3 for high performance and native integration.
- High Performance: Built with Rust and PyO3 for optimal performance
- Comprehensive API: Full access to Calimero Network functionality
- Type Safety: Strongly typed Python bindings
- Async Support: Built-in async/await support
- Easy Installation: Simple pip install
- ✅ NEAR Protocol
- ✅ Ethereum
- ✅ Internet Computer Protocol (ICP)
- ✅ Starknet
- ❌ Stellar (removed)
import asyncio
from calimero_client_py import create_connection, create_client, AuthMode
async def main():
# Create a connection
connection = create_connection(
api_url="https://test.merod.dev.p2p.aws.calimero.network",
node_name="test-dev-node"
)
# Create a client
client = create_client(connection)
# Use the client
contexts = await client.list_contexts()
print(f"Found {len(contexts)} contexts")
if __name__ == "__main__":
asyncio.run(main())pip install calimero-client-py# Install dependencies
pip install maturin
# Build the package
maturin build --release
# Install in development mode
maturin developpython scripts/run_tests.pycalimero-client-py/
├── README.md # This file
├── LICENSE # License file
├── pyproject.toml # Python package configuration
├── Cargo.toml # Rust package configuration
├── Cargo.lock # Rust lock file
├── .github/
│ └── workflows/ # CI/CD workflows
├── src/ # Rust source code
│ ├── lib.rs # PyO3 module registration
│ ├── error.rs # PyClientError wrapper
│ ├── auth.rs # PyAuthMode wrapper
│ ├── token.rs # PyJwtToken wrapper
│ ├── cache.rs # Token cache path utilities
│ ├── storage.rs # MeroboxFileStorage implementation
│ ├── connection.rs # PyConnectionInfo + create_connection()
│ ├── client.rs # PyClient + create_client()
│ └── utils.rs # JSON to Python conversion helpers
├── calimero/ # Python package
│ ├── __init__.py
│ └── cli.py
├── tests/ # All tests
│ ├── test_integration.py
│ └── conftest.py
└── scripts/ # Build and utility scripts
├── build.sh
└── run_tests.py
The Python bindings for calimero-client are ready to build. Stellar support has been removed from the codebase.
- Python Version: 3.13.7
- Virtual Environment: ✅ Active and working
- Maturin: ✅ Installed (version 1.9.4)
- PyO3: ✅ Version 0.20.3 (compatible with Python 3.13 via ABI3)
- ✅ Python 3.13 virtual environment
- ✅ Maturin build system
- ✅ PyO3 configuration
- ✅ Basic project structure
- ✅ Rust toolchain
- ✅ All supported protocol dependencies
# From calimero-client-py root directory
./scripts/build.sh
# Or with options
./scripts/build.sh --install # Build and install in development mode# Install maturin if you haven't already
pip install maturin
# Build the wheel
maturin build --release
# Install in development mode
maturin develop --releaseThis package can be built independently without requiring the full Calimero workspace.
- Rust toolchain (1.70+)
- Python 3.8+
- maturin (will be installed automatically if missing)
✅ Independent builds - No need for full workspace
✅ Easier distribution - Can be built anywhere
✅ CI/CD friendly - Simpler build pipelines
✅ Version control - Clear dependency management
✅ Faster builds - Only builds what's needed
The bindings depend on these crates (published to crates.io):
calimero-client- Core client functionalitycalimero-primitives- Core data types
# Test Rust code
cargo test
# Test Python integration
python scripts/run_tests.py
# Test the environment
source venv/bin/activate.fish
python test-python.pyThis guide explains how to publish the calimero-client-py package to PyPI and other Python package repositories.
- Create an account on PyPI
- Enable two-factor authentication (2FA) for security
- Create an API token for automated publishing
- Create an account on Test PyPI
- Use this for testing before publishing to production
# Install publishing tools
pip install twine build
# Install maturin for Rust bindings
pip install maturin# Remove previous builds
rm -rf target/wheels/
rm -rf dist/
rm -rf build/
rm -rf *.egg-info/# Build the Python wheel
maturin build --features python --release
# Verify the wheel was created
ls -la target/wheels/# Build source distribution
python -m build --sdist
# Verify source distribution
ls -la dist/# Install from wheel
pip install target/wheels/calimero_client_py-*.whl
# Test import
python -c "import calimero_client_py; print('Import successful')"
# Test CLI
calimero-client-py --help# Upload to Test PyPI
twine upload --repository testpypi target/wheels/*.whl
# Install from Test PyPI
pip install --index-url https://test.pypi.org/simple/ calimero-client-py
# Test functionality
python -c "import calimero_client_py; print('Test PyPI install successful')"# Check package metadata
twine check target/wheels/*.whl
# Verify package contents
pip show calimero-client-py# Upload to production PyPI
twine upload target/wheels/*.whl
# Or upload both wheel and source
twine upload dist/*# Wait a few minutes for PyPI to update
# Check on https://pypi.org/project/calimero-client-py/
# Test installation from PyPI
pip install calimero-client-py
# Verify it works
python -c "import calimero_client_py; print('PyPI install successful')"The package includes a GitHub Actions workflow that automatically publishes when you create a release tag.
# Tag the release
git tag -a v0.1.0 -m "Release version 0.1.0"
# Push the tag
git push origin v0.1.0In your GitHub repository settings, add these secrets:
PYPI_API_TOKEN: Your PyPI API tokenTEST_PYPI_API_TOKEN: Your Test PyPI API token (optional)
- Check the Actions tab in GitHub
- The workflow will automatically:
- Build the package
- Run tests
- Publish to PyPI
# Check Rust toolchain
rustup show
# Update dependencies
cargo update
# Clean and rebuild
cargo clean
maturin build --features python# Verify module structure
python -c "import sys; print(sys.path)"
# Check package installation
pip list | grep calimero# Check authentication
twine check target/wheels/*.whl
# Verify API token
echo $PYPI_API_TOKEN
# Test with Test PyPI first
twine upload --repository testpypi target/wheels/*.whl# Update version in all files:
# - pyproject.toml
# - Cargo.toml
# - calimero_client_py/__init__.py
# - README.md (if version is mentioned)# Update Rust dependencies
cargo update
# Update Python dependencies
pip install --upgrade -r requirements-dev.txt
# Test with updated dependencies
pytest# Check for security vulnerabilities
safety check
# Update vulnerable packages
pip install --upgrade package-name- Build fails: Make sure
calimero-clientis published to crates.io - Import errors: Verify the wheel was built for your Python version
- Runtime errors: Check that all dependencies are properly linked
The client library includes automatic JWT token persistence for authenticated sessions. This feature enables:
- Automatic token loading: Tokens are loaded from disk when creating a connection
- Automatic token refresh: On 401 errors, the client automatically calls
/auth/refreshand persists new tokens - Secure storage: Tokens are stored with restrictive file permissions (0600 on Unix)
Tokens are stored in ~/.merobox/auth_cache/ with filenames derived from the node_name:
~/.merobox/auth_cache/{sanitized_node_name}-{hash}.json
The node_name parameter is critical for authenticated connections:
from calimero_client_py import create_connection, create_client
# For authenticated connections, node_name MUST be:
# 1. Provided (not None)
# 2. Stable across sessions (same value each time)
# 3. Unique per remote node you connect to
connection = create_connection(
api_url="https://my-node.example.com:2428",
node_name="my-production-node" # Use a stable, descriptive name
)Important considerations:
| Requirement | Why |
|---|---|
| Stable | The same node_name must be used across sessions to find cached tokens |
| Unique | Different nodes should have different names to avoid token collisions |
| Descriptive | Use meaningful names like "prod-node-1" or "dev-local" for clarity |
Two helper functions are exposed for managing the token cache:
from calimero_client_py import get_token_cache_path, get_token_cache_dir
# Get the full path to a node's token file
path = get_token_cache_path("my-node")
# Returns: ~/.merobox/auth_cache/my-node-<hash>.json
# Get the base cache directory
cache_dir = get_token_cache_dir()
# Returns: ~/.merobox/auth_cache/-
Initial authentication (handled by your application, e.g., merobox):
- Call the auth endpoint to obtain JWT tokens
- Write tokens to the path returned by
get_token_cache_path(node_name)
-
Subsequent connections:
- Create connection with the same
node_name - Client automatically loads tokens from cache
- Client attaches
Authorization: Bearer ...header to requests
- Create connection with the same
-
Token refresh (automatic):
- On 401 response, client calls
/auth/refresh - New tokens are automatically persisted to the same cache file
- On 401 response, client calls
import json
from calimero_client_py import (
create_connection,
create_client,
get_token_cache_path,
)
NODE_NAME = "my-remote-node" # Keep this stable!
# Step 1: Write initial tokens (done once after authentication)
def save_initial_tokens(access_token, refresh_token, expires_at):
token_data = {
"access_token": access_token,
"refresh_token": refresh_token,
"expires_at": expires_at
}
cache_path = get_token_cache_path(NODE_NAME)
# Ensure directory exists
import os
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
with open(cache_path, 'w') as f:
json.dump(token_data, f)
# Step 2: Create authenticated client (tokens loaded automatically)
connection = create_connection(
api_url="https://my-node.example.com:2428",
node_name=NODE_NAME
)
client = create_client(connection)
# Step 3: Use the client - Authorization header is set automatically
contexts = client.list_contexts()Client: Main client for interacting with Calimero NetworkConnectionInfo: Connection configurationJwtToken: JWT authentication tokenClientError: Error handlingAuthMode: Authentication modes
create_connection(): Create a new connectioncreate_client(): Create a new client instance
The Client class provides comprehensive access to Calimero Network functionality:
get_api_url(): Get the API URL for this clientget_peers_count(): Get the number of connected peers
get_application(app_id: str): Get information about a specific applicationlist_applications(): List all available applicationsinstall_application(url: str, hash: Optional[str], metadata: Optional[bytes]): Install application from URLinstall_dev_application(path: str, metadata: Optional[bytes]): Install development application from local pathuninstall_application(app_id: str): Uninstall an application
get_context(context_id: str): Get information about a specific contextlist_contexts(): List all available contextscreate_context(application_id: str, protocol: str, params: Optional[str]): Create a new contextdelete_context(context_id: str): Delete a contextsync_context(context_id: str): Sync a specific contextsync_all_contexts(): Sync all contexts
get_context_storage(context_id: str): Get context storage informationget_context_identities(context_id: str): Get identities associated with a contextget_context_client_keys(context_id: str): Get client keys for a contextinvite_to_context(context_id: str, inviter_id: str, invitee_id: str): Invite someone to a contextjoin_context(context_id: str, invitee_id: str, invitation_payload: str): Join a context using invitationupdate_context_application(context_id: str, application_id: str, executor_public_key: str): Update context application
execute_function(context_id: str, method: str, args: str, executor_public_key: str): Execute a function call via JSON-RPC
grant_permissions(context_id: str, permissions: str): Grant permissions to users in a contextrevoke_permissions(context_id: str, permissions: str): Revoke permissions from users in a context
get_proposal(context_id: str, proposal_id: str): Get proposal informationget_proposal_approvers(context_id: str, proposal_id: str): Get proposal approverslist_proposals(context_id: str, args: Optional[str]): List proposals in a context
generate_context_identity(): Generate a new context identity
list_blobs(): List all blobsget_blob_info(blob_id: str): Get information about a specific blobdelete_blob(blob_id: str): Delete a blob
create_context_identity_alias(context_id: str, alias: str, public_key: str): Create context identity aliascreate_context_alias(alias: str, context_id: str): Create context aliascreate_application_alias(alias: str, application_id: str): Create application aliasdelete_context_alias(alias: str): Delete context aliasdelete_context_identity_alias(alias: str, context_id: str): Delete context identity alias
All client methods return Python objects (dictionaries, lists, etc.) containing the response data. The exact structure depends on the specific method called, but generally follows these patterns:
{
"success": True,
"data": { ... }, # Method-specific data
"message": "Operation completed successfully"
}{
"success": False,
"error": "Error description",
"error_type": "Network|Authentication|Storage|Internal"
}import asyncio
from calimero_client_py import create_connection, create_client, AuthMode
async def main():
# Create connection
connection = create_connection(
api_url="https://test.merod.dev.p2p.aws.calimero.network",
node_name="test-dev-node"
)
# Create client
client = create_client(connection)
# List applications
apps = client.list_applications()
print(f"Found {len(apps)} applications")
# Create a context
context = client.create_context(
application_id="my-app-id",
protocol="near",
params='{"network": "testnet"}'
)
print(f"Created context: {context}")
# Execute a function
result = client.execute_function(
context_id=context["context_id"],
method="set_value",
args='{"key": "test", "value": "hello"}',
executor_public_key="your-public-key"
)
print(f"Function result: {result}")
if __name__ == "__main__":
asyncio.run(main())If you encounter issues during publishing:
- Check the GitHub Issues
- Review the GitHub Actions logs
- Contact the team at team@calimero.network
Happy Publishing! 🎉