Skip to main content

An async client library for the Gemini protocol

Project description

Wasat: Async Gemini Protocol Client Library

Wasat is a fully type-hinted asynchronous client library for the Gemini Protocol, relying only on cryptography for certificate management.

Features

  • Async All the Way: Built on top of Python's standard asyncio loop with streaming/chunking support.
  • Type Safe: Fully typed API.
  • 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.
  • Minimal Dependencies: Relies only on cryptography for certificate management, otherwise using Python's standard library.
  • CLI Utility: Includes a wasat command-line interface out of the box.

Installation

wasat is available from pypi and can be installed with your package installer of choice.

With pip:

pip install wasat

With uv:

uv add wasat

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.3.0.tar.gz (19.4 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.3.0-py3-none-any.whl (23.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: wasat-0.3.0.tar.gz
  • Upload date:
  • Size: 19.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","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.3.0.tar.gz
Algorithm Hash digest
SHA256 f5639ef07f889cd7b07fd11f2b4a45f438c84e4ee5b8b6b8f0d66639998cbc86
MD5 7aea8f167caba7006973d8156ba1847c
BLAKE2b-256 8668d00cba89f6b5f8dc50251db2547b5b954ec258850a5e6d9405718cc041b7

See more details on using hashes here.

File details

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

File metadata

  • Download URL: wasat-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 23.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","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.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b734cbfe680181999832bb94ed021b379f20c1b4d3510fed1a758157de12a4ca
MD5 f7ef8234bbf31d67e7473a2a1da124ef
BLAKE2b-256 2060fa0b2bc90f33c2ad02941ab437d87e7379eb022f7c2e1e1b6c6e5ba3cf82

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