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
asyncioloop 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
wasatcommand-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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2da8445742cb2c4a6a7ccc8ab9b3c6ae269e0bc3ba83c724b7fdbf60940ef1b5
|
|
| MD5 |
8d5fa682970da8b018ecfb601f40f7cc
|
|
| BLAKE2b-256 |
7e63c39a8225706bb14f251ab5b6747575c6b6a0720e4ac733f81a9d4d336cd6
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe45bc8918db1e0f11c4c0feac2747d76dd56b8210a07b5bb5dd11ecbccc4390
|
|
| MD5 |
c29158b9a94c528e0b6b165022a2b955
|
|
| BLAKE2b-256 |
06e6f2f8955c431dfb0c58b38abcac1826e87cb4eedd8e4a340f354705fa20d4
|