We welcome contributions to Routstr Proxy! This document provides guidelines and instructions for contributing to the project.
- Getting Started
- Development Setup
- Code Standards
- Testing
- Submitting Changes
- Project Structure
- Documentation
- Release Process
- Python 3.11 or higher
- uv package manager
- Docker and Docker Compose (optional, for integration tests)
- Git
-
Fork and clone the repository
git clone https://github.com/YOUR_USERNAME/routstr-proxy.git cd routstr-proxy -
Set up the development environment
make setup
This will:
- Install
uvif not already installed - Create a virtual environment
- Install all dependencies including dev tools
- Install the project in editable mode
- Install
-
Configure environment variables
cp .env.example .env # Edit .env with your configuration -
Verify your setup
make check-deps make test-unit
We use modern Python 3.11+ features and enforce strict type checking:
-
Type Hints: All functions must have complete type annotations
# ✅ Good def calculate_cost(tokens: int, price_per_token: float) -> dict[str, float]: return {"total": tokens * price_per_token} # ❌ Bad def calculate_cost(tokens, price_per_token): return {"total": tokens * price_per_token}
-
Type Syntax: Use Python 3.11+ lowercase types
# ✅ Good def process_items(items: list[dict[str, str | None]]) -> dict[str, int]: ... # ❌ Bad from typing import List, Dict, Optional def process_items(items: List[Dict[str, Optional[str]]]) -> Dict[str, int]: ...
-
Comments: Only add comments for non-obvious logic. Code should be self-documenting
# ✅ Good - complex business logic explained # Apply exponential backoff with jitter to prevent thundering herd delay = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay) # ❌ Bad - obvious comment # Increment counter by 1 counter += 1
We enforce code quality using:
-
Ruff: For linting and formatting
make lint # Check for issues make format # Auto-fix formatting
-
Mypy: For type checking
make type-check
Follow the Conventional Commits specification:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Test additions or fixeschore: Build process or auxiliary tool changes
Examples:
feat(proxy): add support for streaming responses
fix(wallet): handle expired tokens correctly
docs: update API documentation for v2 endpoints
Tests are organized into:
tests/unit/- Fast, isolated unit teststests/integration/- Integration tests (can use mocks or real services)
# Run all tests (unit + integration with mocks)
make test
# Run specific test suites
make test-unit # Unit tests only
make test-integration # Integration tests with mocks
make test-integration-docker # Integration tests with real services
make test-performance # Performance benchmarks
# Advanced testing
make test-coverage # Generate coverage report
make test-fast # Skip slow tests
make test-failed # Re-run only failed tests-
Use pytest fixtures for reusable test setup
-
Mark async tests with
@pytest.mark.asyncio -
Use appropriate markers:
@pytest.mark.slow @pytest.mark.requires_docker async def test_complex_integration(): ...
-
Follow the AAA pattern: Arrange, Act, Assert
async def test_token_validation(): # Arrange token = create_test_token(amount=1000) # Act result = await validate_token(token) # Assert assert result.is_valid assert result.amount == 1000
-
Create a feature branch
git checkout -b feat/your-feature-name
-
Make your changes
- Write code following our standards
- Add or update tests
- Update documentation if needed
-
Run quality checks
make lint make type-check make test -
Commit your changes
- Use conventional commit messages
- Keep commits focused and atomic
-
Push and create a PR
- Push to your fork
- Create a PR against the
mainbranch - Fill out the PR template completely
- Link any related issues
Before requesting review, ensure:
- All tests pass
- Code follows style guidelines
- Type hints are complete and correct
- Documentation is updated
- Commit messages follow conventions
- No unnecessary changes outside scope
- Reviews typically happen within 2-3 business days
- Be prepared to make changes based on feedback
- Engage constructively in discussions
- Once approved, a maintainer will merge your PR
routstr-proxy/
├── routstr/ # Main application code
│ ├── core/ # Core functionality
│ │ ├── admin.py # Admin interface
│ │ ├── db.py # Database models and operations
│ │ ├── logging.py # Logging configuration
│ │ └── main.py # FastAPI app initialization
│ ├── payment/ # Payment processing
│ │ ├── cost_calculation.py
│ │ ├── models.py
│ │ └── x_cashu.py # Cashu integration
│ ├── auth.py # Authentication
│ ├── proxy.py # Request proxying logic
│ └── wallet.py # Wallet management
├── tests/ # Test suite
│ ├── unit/ # Unit tests
│ └── integration/ # Integration tests
├── scripts/ # Utility scripts
├── compose.yml # Docker compose for production
├── compose.testing.yml # Docker compose for testing
├── Makefile # Development commands
└── pyproject.toml # Project configuration
- FastAPI Application: Main API server in
routstr/core/main.py - Database Models: SQLModel definitions in
routstr/core/db.py - Payment Logic: Cashu integration and cost calculation in
routstr/payment/ - Proxy Handler: Request forwarding logic in
routstr/proxy.py
-
Use descriptive variable and function names
-
Add docstrings for public APIs:
async def redeem_token(token: str, mint_url: str) -> RedemptionResult: """Redeem a Cashu token and credit the account. Args: token: Base64-encoded Cashu token mint_url: URL of the Cashu mint Returns: RedemptionResult with amount and status Raises: TokenInvalidError: If token is malformed or expired MintConnectionError: If mint is unreachable """
- Update OpenAPI schemas when adding endpoints
- Keep
README.mdexamples current - Document environment variables in
.env.example
For significant changes, create an ADR (Architecture Decision Record) in docs/adr/:
# ADR-001: Use SQLite for Local Storage
## Status
Accepted
## Context
We need a simple, embedded database for storing API keys and balances.
## Decision
Use SQLite with SQLModel ORM for type safety and async support.
## Consequences
- No external database required
- Simple deployment
- Limited concurrent write performanceWe use Semantic Versioning:
- MAJOR: Breaking API changes
- MINOR: New features, backwards compatible
- PATCH: Bug fixes and minor improvements
- Update version in
pyproject.toml - Update
CHANGELOG.mdwith release notes - Create a git tag:
git tag -a v1.2.3 -m "Release v1.2.3" - Push tag:
git push origin v1.2.3 - GitHub Actions will build and publish Docker images
- Issues: Check existing issues or create a new one
- Discussions: Use GitHub Discussions for questions
- Security: Report security issues privately to maintainers
By contributing, you agree that your contributions will be licensed under the GPLv3 license.