Skip to content
This repository was archived by the owner on Mar 12, 2026. It is now read-only.

t-0-network/provider-python

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

⚠️ DEPRECATED

This repository has been deprecated. All development has moved to the unified Provider SDK monorepo.

Please use t-0-network/provider-sdk instead.

This repository is no longer maintained and will be archived.


T-0 Network Provider SDK for Python

Python SDK and project initializer for building T-0 Network payment providers. The repository contains two packages:

  • t0-provider-sdk -- SDK providing ConnectRPC communication, secp256k1 cryptographic signing/verification, and ASGI/WSGI middleware for signature validation.
  • t0-provider-starter -- CLI tool that scaffolds a complete provider project with sensible defaults.

Prerequisites

  • Python >= 3.13
  • uv -- dependency management and runner
  • Docker (optional, for containerized deployment)

Quick Start

  1. Create a new provider project:

    uvx t0-provider-starter my_provider

    Alternative (install first, then run):

    pip install t0-provider-starter
    t0-provider-starter my_provider
  2. Install dependencies:

    cd my_provider
    uv sync
  3. Configure environment:

    Edit .env -- the PROVIDER_PRIVATE_KEY is auto-generated. Set NETWORK_PUBLIC_KEY (provided by the T-0 team) and optionally adjust TZERO_ENDPOINT.

  4. Start the server:

    uv run python -m provider.main

    The generated project uses an async ASGI server (uvicorn) by default. See WSGI Alternative below if you prefer a synchronous server.

  5. Share your public key (printed during project initialization) with the T-0 team.

CLI Options

t0-provider-starter <project_name> [-d <directory>]
Argument / Option Required Default Description
project_name Yes -- Name for pyproject.toml and the project directory
-d, --directory No ./<project_name> Target directory for the generated project

The CLI generates a secp256k1 keypair, writes the private key to .env, and prints the corresponding public key.

Generated Project Structure

my_provider/
├── pyproject.toml              # Project metadata, depends on t0-provider-sdk
├── Dockerfile                  # Multi-stage build with python:3.13-slim
├── .env.example                # Template environment file
├── .env                        # Generated with your private key (git-ignored)
└── src/provider/
    ├── __init__.py
    ├── main.py                 # Entry point: server, quote publishing, quote retrieval
    ├── config.py               # Environment variable loading and validation
    ├── publish_quotes.py       # Sample quote publishing loop
    ├── get_quote.py            # Sample quote retrieval
    └── handler/
        ├── __init__.py
        ├── payment.py          # ProviderServiceImplementation (async, 5 RPC methods)
        └── payment_sync.py     # ProviderServiceSyncImplementation (sync/WSGI variant)

The primary file to modify is src/provider/handler/payment.py (async) or src/provider/handler/payment_sync.py (sync/WSGI), which contain stub implementations for all provider RPC methods.

Environment Variables

Variable Required Default Description
PROVIDER_PRIVATE_KEY Yes -- secp256k1 private key (hex, 0x-prefixed). Auto-generated by the CLI.
NETWORK_PUBLIC_KEY Yes Sandbox key (pre-filled) T-0 Network public key for verifying inbound request signatures. Update for production.
TZERO_ENDPOINT No https://api-sandbox.t-0.network T-0 Network API base URL
PORT No 8080 Server listen port
QUOTE_PUBLISHING_INTERVAL No 5000 Interval in milliseconds between quote publications

Integration Steps

The generated template contains numbered TODO comments that guide you through the integration process. Follow them in order:

Phase 1: Quoting

  1. Step 1.1 -- Initialize and start the server (completed after Quick Start). See src/provider/main.py.

  2. Step 1.2 -- Share the generated public key from .env with the T-0 team.

  3. Step 1.3 -- Replace the sample quote publishing logic with your own. See src/provider/publish_quotes.py.

  4. Step 1.4 -- Verify that quotes for your target currency are successfully received. See src/provider/get_quote.py.

Phase 2: Payments

  1. Step 2.1 -- Implement update_payment to handle updates for payments you initiate. See src/provider/handler/payment.py.

  2. Step 2.2 -- Deploy your integration and share the base URL with the T-0 team.

  3. Step 2.3 -- Test payment submission end-to-end.

  4. Step 2.4 -- Implement pay_out to handle payouts initiated by counterparts. See src/provider/handler/payment.py.

  5. Step 2.5 -- Ask the T-0 team to submit a test payout to verify your pay_out endpoint.

Additional optional methods in src/provider/handler/payment.py:

  • update_limit -- handle notifications about limit changes
  • append_ledger_entries -- handle notifications about ledger transactions
  • approve_payment_quotes -- approve quotes after AML check

WSGI Alternative

The default generated project uses async ASGI (uvicorn). If you prefer a synchronous WSGI server (e.g. gunicorn, waitress), replace the ASGI setup in src/provider/main.py:

from t0_provider_sdk.api.tzero.v1.payment.provider_connect import ProviderServiceWSGIApplication
from t0_provider_sdk.provider import handler_sync, new_wsgi_app
from provider.handler.payment_sync import ProviderServiceSyncImplementation

def create_provider_app(config, network_client_sync):
    service = ProviderServiceSyncImplementation(network_client_sync)
    return new_wsgi_app(
        config.network_public_key,
        handler_sync(ProviderServiceWSGIApplication, service),
    )

Then run with a WSGI server:

gunicorn provider.main:app --bind 0.0.0.0:8080

The sync variant uses payment_sync.py instead of payment.py -- implement the same RPC methods as regular def functions instead of async def.

Available Commands

Run these inside the generated project directory:

uv run python -m provider.main    # Start the provider server
uv run pytest                     # Run tests
uv run ruff check .               # Lint

Deployment

Docker

The generated project includes a multi-stage Dockerfile using python:3.13-slim:

docker build -t my-provider .
docker run --env-file .env -p 8080:8080 my-provider

Production Checklist

  • Set TZERO_ENDPOINT to the production API URL
  • Ensure NETWORK_PUBLIC_KEY matches the production network key
  • Keep PROVIDER_PRIVATE_KEY in a secrets manager, not in plaintext files
  • Synchronize server clock via NTP (signature timestamps are validated)

Security Considerations

  • Private key protection: PROVIDER_PRIVATE_KEY must never be committed to version control. Add .env to your .gitignore.
  • Signature verification: All inbound requests from the T-0 Network are verified against NETWORK_PUBLIC_KEY via ASGI or WSGI middleware. Verification uses raw request body bytes, not re-serialized protobuf.
  • Timestamp validation: Request timestamps must be within +/- 60 seconds of server time. Keep system clocks synchronized.
  • Body size limit: Inbound request bodies are limited to 4 MB by default.

Troubleshooting

PROVIDER_PRIVATE_KEY is not set in .env

Copy .env.example to .env and set PROVIDER_PRIVATE_KEY. The CLI generates this automatically; if you created the project manually, generate a secp256k1 private key.

ModuleNotFoundError: No module named 'provider'

Run uv sync in the generated project directory. The project uses a src/ layout that requires installation.

Signature verification failures

Ensure the server clock is synchronized (NTP). Timestamps outside +/- 60 seconds are rejected. Verify that NETWORK_PUBLIC_KEY matches the key provided by the T-0 team.

pip install connectrpc installs the wrong package

The correct PyPI package is connect-python (which imports as connectrpc). The connectrpc package on PyPI is a different, unmaintained package by a third party. The SDK's pyproject.toml already depends on the correct package.

Support

Contact the T-0 team for integration support, API credentials, and production onboarding.


Maintainer Guide

Everything below is for SDK and starter maintainers, not end users.

Repository Structure

provider-python/
├── pyproject.toml                  # uv workspace root
├── sdk/                            # t0-provider-sdk package
│   ├── pyproject.toml
│   ├── buf.yaml + buf.gen.yaml     # Proto code generation config
│   └── src/t0_provider_sdk/
│       ├── api/                    # Generated ConnectRPC stubs (committed)
│       ├── proto/                  # Proto definitions (source of truth)
│       ├── crypto/                 # hash, keys, signer, verifier
│       ├── common/                 # header constants
│       ├── network/                # signing transport, client factory
│       └── provider/               # middleware, interceptor, handler
├── starter/                        # t0-provider-starter CLI package
│   ├── pyproject.toml
│   └── src/t0_provider_starter/
│       ├── cli.py                  # Click-based CLI entry point
│       ├── keygen.py               # secp256k1 keypair generation
│       └── template/               # Embedded project template files
├── tests/
│   └── cross_test/                 # Go SDK interop tests
│       ├── go_helper/              # Go binary for cross-testing
│       ├── test_cross_signature.py # Crypto interop (hash, sign, verify)
│       ├── test_cross_server.py    # ASGI server-to-server
│       └── test_cross_server_sync.py # WSGI server-to-server
└── docs/
    └── PITFALLS.md                 # Known issues and workarounds

This is a uv workspace with two members: sdk and starter.

Building

uv sync --all-packages

Installs all workspace dependencies including dev tools (pytest, ruff, uvicorn, protoc-gen-connect-python).

Testing

uv run pytest -v                           # All tests (SDK + integration + cross)
uv run pytest sdk/tests -v                 # SDK unit tests only
uv run pytest sdk/tests/integration -v     # Integration tests only
uv run pytest tests/cross_test -v          # Go cross-tests (requires Go helper)

Cross-Tests with Go SDK

These tests validate interoperability between the Python and Go SDKs: Keccak256 hash equivalence, public key derivation, bidirectional signature verification, and end-to-end server-to-server communication (both ASGI and WSGI).

cd tests/cross_test/go_helper && go build -o go_helper . && cd ../../..
uv run pytest tests/cross_test/ -v

Linting

uv run ruff check .

Configuration in root pyproject.toml: target Python 3.13, line length 120, rules: E, F, W, I, N, UP, B, A, SIM, TCH.

Proto Code Generation

When proto definitions change, regenerate the Python + ConnectRPC stubs:

cd sdk
buf dep update
buf generate
  • Proto source files: sdk/src/t0_provider_sdk/proto/
  • Generated output: sdk/src/t0_provider_sdk/api/
  • Generated code is committed to the repository
  • After regeneration, verify that api/buf/__init__.py and api/buf/validate/__init__.py exist

Architecture: Go SDK Mapping

Reference for porting changes from the Go SDK:

Go SDK Python SDK
crypto/hash.go sdk/src/t0_provider_sdk/crypto/hash.py
crypto/sign.go sdk/src/t0_provider_sdk/crypto/signer.py
crypto/verify_signature.go sdk/src/t0_provider_sdk/crypto/verifier.py
crypto/helper.go sdk/src/t0_provider_sdk/crypto/keys.py
common/header.go sdk/src/t0_provider_sdk/common/headers.py
network/signing_transport.go sdk/src/t0_provider_sdk/network/signing.py
network/client.go sdk/src/t0_provider_sdk/network/client.py
provider/verify_signature.go sdk/src/t0_provider_sdk/provider/middleware.py (ASGI), middleware_wsgi.py (WSGI)
provider/signature_error.go sdk/src/t0_provider_sdk/provider/interceptor.py
provider/handler.go sdk/src/t0_provider_sdk/provider/handler.py

Key Dependencies

PyPI Package Import Purpose Notes
connect-python connectrpc ConnectRPC runtime Do NOT use the connectrpc PyPI package (different, unmaintained)
protobuf google.protobuf Message serialization >= 5.28 required
coincurve coincurve secp256k1 ECDSA Signing, verification, key derivation
pycryptodome Crypto.Hash.keccak Keccak256 hash Do NOT use pysha3 (incompatible with Python 3.13) or hashlib.sha3_256 (different padding)
pyqwest pyqwest HTTP client (Rust-backed) Transitive dependency via connect-python

See docs/PITFALLS.md for a comprehensive list of known issues and workarounds.

Releasing

Publishing is fully automated via GitHub Actions:

  1. Go to Actions → Create Release → Run workflow (or gh workflow run "Create Release").
  2. The release workflow bumps the patch version in both sdk/pyproject.toml and starter/pyproject.toml, commits, tags, and creates a GitHub Release.
  3. The tag push triggers the Publish Packages workflow, which:
    • Publishes t0-provider-sdk to PyPI (environment: pypi-sdk)
    • Publishes t0-provider-starter to PyPI (environment: pypi-starter)
    • Uploads wheels and sdists to the GitHub Release

PyPI authentication uses Trusted Publishers (OIDC) — no API tokens needed.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors