Skip to main content

A async client library for the Gemini protocol

Project description

Wasat: Async Gemini Protocol Client Library

Wasat is an object-oriented, fully type-hinted asynchronous client library for the Gemini Protocol, built with zero external dependencies.

Features

  • Async All the Way: Built on top of Python's standard asyncio loop with streaming/chunking support.
  • Type Safe: Fully typed API utilizing Python 3.14 standards (strictly avoiding Any).
  • TOFU (Trust-On-First-Use) Support: Secure by default with a built-in file-based TOFU store and custom interactive trust confirmation hooks.
  • Auto Redirect Handling: Automatically handles temporary and permanent redirects (with protection against loops and infinite redirect limits), caching permanent redirects locally.
  • Client Authentication: Native support for client TLS certificates.
  • Zero-Dependency: Runs purely on Python's standard library.
  • CLI Utility: Includes a wasat command-line interface out of the box.

Installation

You can install wasat directly in your project virtual environment using uv or pip:

uv pip install -e .
# or
pip install -e .

Quick Start

1. Make a Simple Request

Use Client with standard async context managers to query a Gemini capsule and decode the response:

import asyncio
from wasat import Client, WasatError

async def main():
    # 'tofu' mode is ideal for standard Gemini capsules (self-signed certs)
    client = Client(verify_mode="tofu")

    try:
        # Perform the request (automatically resolves host, port, TLS, and redirects)
        async with await client.request("gemini://geminiprotocol.net/") as response:
            print(f"Status: {response.status.value} ({response.status.name})")
            print(f"MIME type: {response.mime_type}")

            # Fetch the decoded body text
            body = await response.text()
            print(body)

    except WasatError as e:
        print(f"Request failed: {e}")

if __name__ == "__main__":
    asyncio.run(main())

2. Streaming Chunk Responses

For large files or media streams, read the response body in chunks to prevent exhausting memory:

async with await client.request("gemini://example.com/large-file.txt") as response:
    if response.status.is_success:
        async for chunk in response.iter_chunks(chunk_size=1024):
            # Process each chunk as it arrives
            sys.stdout.buffer.write(chunk)

3. Client Certificate Authentication

If a server requires client auth (status code 60), supply your certificate files to the client configuration:

client = Client(
    verify_mode="tofu",
    client_cert="/path/to/client.crt",
    client_key="/path/to/client.key"  # Optional if key is embedded in cert
)

4. Interactive TOFU Confirmation

Implement a custom asynchronous callback to prompt the user before trusting new self-signed certificates:

import sys

async def confirm_cert(host: str, port: int, fingerprint: str) -> bool:
    print(f"New certificate encountered for {host}:{port}")
    print(f"Fingerprint: sha256:{fingerprint}")
    response = input("Do you trust this certificate? [y/N]: ").strip().lower()
    return response == "y"

client = Client(
    verify_mode="tofu",
    on_new_certificate=confirm_cert
)

Command Line Interface (CLI)

Wasat comes with a command-line interface to fetch Gemini capsules from your shell:

# Basic fetch using the entrypoint script (uses TOFU)
uv run wasat gemini://geminiprotocol.net/

# Alternatively, execute the package directly using python -m
uv run python -m wasat gemini://geminiprotocol.net/

# Fetch a local or custom port capsule
uv run wasat gemini://localhost:1965/index.gmi

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wasat-0.0.1.tar.gz (12.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wasat-0.0.1-py3-none-any.whl (15.9 kB view details)

Uploaded Python 3

File details

Details for the file wasat-0.0.1.tar.gz.

File metadata

  • Download URL: wasat-0.0.1.tar.gz
  • Upload date:
  • Size: 12.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for wasat-0.0.1.tar.gz
Algorithm Hash digest
SHA256 2da8445742cb2c4a6a7ccc8ab9b3c6ae269e0bc3ba83c724b7fdbf60940ef1b5
MD5 8d5fa682970da8b018ecfb601f40f7cc
BLAKE2b-256 7e63c39a8225706bb14f251ab5b6747575c6b6a0720e4ac733f81a9d4d336cd6

See more details on using hashes here.

File details

Details for the file wasat-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: wasat-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 15.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for wasat-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fe45bc8918db1e0f11c4c0feac2747d76dd56b8210a07b5bb5dd11ecbccc4390
MD5 c29158b9a94c528e0b6b165022a2b955
BLAKE2b-256 06e6f2f8955c431dfb0c58b38abcac1826e87cb4eedd8e4a340f354705fa20d4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page