Thank you for your interest in contributing! This document provides guidelines and instructions for contributing.
Please be respectful and constructive in all interactions. We're all here to build something useful together.
-
Fork and clone the repository:
git clone https://github.com/YOUR_USERNAME/tailscale-proxy.git cd tailscale-proxy -
Install in editable mode:
pipx install -e . -
Verify installation:
tailscale-proxy self-test
tailscale-proxy/
├── src/proxy_tailscale/
│ ├── cli.py # Main CLI (~3200 lines, single-file architecture)
│ └── ip_reputation.py # IP reputation checker module
├── docs/ # Documentation
├── assets/ # Demo GIFs, images
└── .github/ # GitHub templates and workflows
- Check existing issues first
- Use the bug report template
- Include
tailscale-proxy diagnosticsoutput - Describe steps to reproduce
- Open a feature request issue
- Describe the use case and expected behavior
- Consider if it fits the project's scope (simplifying proxy setup)
-
Create a feature branch:
git checkout -b feature/your-feature-name
-
Make your changes following the code conventions below
-
Test thoroughly:
tailscale-proxy self-test
-
Commit with a clear message:
git commit -m "feat: add XYZ feature" -
Push and open a Pull Request
- Single-file CLI: Most functionality lives in
cli.py - No external test framework: Use
self-testcommand for health checks - Minimal dependencies: Only
typerandrichfor Python deps
Command execution - Always use CmdResult:
def run_cmd(cmd: str | list[str], sudo: bool = False, capture: bool = True) -> CmdResultRich console output - Use Rich markup:
console.print("[green]Success[/green]")
console.print(Panel("Content", title="Title"))Systemd services - Follow naming convention:
SERVICE_NAME = f"{APP_ID}-feature.service"File writes with sudo fallback:
write_file(path: Path, content: str, mode: int = 0o644)- Add function with
@app.command("command-name")decorator - Use
typer.Option()for CLI args with defaults - Add menu entry in
menu()function if user-facing - Follow pattern: check deps → prompt user → execute → report status
Example:
@app.command("my-feature")
def my_feature(
port: int = typer.Option(60000, "--port", "-p", help="Port number"),
no_prompt: bool = typer.Option(False, "--no-prompt", help="Skip confirmation"),
) -> None:
"""Short description of the command."""
# Check dependencies
if not cmd_exists("required_binary"):
print("[red]Missing required_binary[/red]")
raise typer.Exit(1)
# Prompt if needed
if not no_prompt:
if not Confirm.ask("Proceed?"):
raise typer.Exit(0)
# Execute
result = run_cmd(["some", "command"])
# Report
if result.returncode == 0:
print("[green]Done![/green]")
else:
print(f"[red]Failed: {result.stderr}[/red]")- Use type hints for function parameters and return types
- Use
textwrap.dedent()for multi-line config strings - Prefer
Pathobjects over string paths - Use Rich
Panel,Table, and markup for output
Follow Conventional Commits:
feat:- New featurefix:- Bug fixdocs:- Documentation changesrefactor:- Code refactoringchore:- Maintenance tasks
Examples:
feat: add IP reputation checker module
fix: handle missing redsocks config gracefully
docs: update installation instructions
Since there's no formal test suite, please:
- Run
tailscale-proxy self-test - Test on both Debian and Arch if possible
- Verify affected commands work correctly
- Check for regressions in related functionality
- Open a Discussion
- Check existing Issues
Thank you for contributing!