Skip to main content

Free-threading-native QUIC and HTTP/3 for Python 3.14t — sans-I/O, typed

Project description

⟢⟣ Zoomies

PyPI version Build Status Python 3.14+ License: MIT Status: Beta

Free-threading-native QUIC and HTTP/3 for Python 3.14t — sans-I/O, typed

Using zoomies from an AI agent (or moving fast)? See CLAUDE.md for a concise library-user guide: mental model, timing contract, event cheatsheet, and foot-guns.

import time
from zoomies.core import QuicConnection, QuicConfiguration
from zoomies.events import HandshakeComplete

config = QuicConfiguration(certificate=cert, private_key=key)
conn = QuicConnection(config)

# Sans-I/O: feed datagrams in, get events out
now = time.monotonic()
events = conn.datagram_received(datagram, addr, now=now)
for event in events:
    if isinstance(event, HandshakeComplete):
        ...
for dg in conn.send_datagrams(now=now):
    sock.sendto(dg, addr)

What is Zoomies?

Zoomies is a sans-I/O protocol library for QUIC (RFC 9000) and HTTP/3 (RFC 9114). Native to the b-stack (Pounce, Chirp), it has no b-stack dependencies and works anywhere — pure Python, cryptography only, free-threaded Python 3.14t. Alpha: full TLS 1.3 handshake, 1-RTT packets, loss recovery (RFC 9002), and congestion control.

What's good about it:

  • Sans-I/O — Protocol layer consumes bytes, produces bytes. No socket access. Caller owns I/O.
  • Types as contracts — Frozen dataclasses for events, Protocols for handlers.
  • Free-threading native — No C extensions with limited API. Uses cryptography (3.14t-compatible).
  • Composition — Packet → Crypto → Stream → Connection → Recovery → HTTP/3. Each layer testable in isolation.
  • Loss recovery — RFC 9002 loss detection, RTT estimation, NewReno congestion control. Built into the connection layer.

What it does

API Description
QuicConnection.datagram_received(data, addr, now=) Feed UDP datagram in, get protocol events
QuicConnection.send_datagrams(now=) Get outbound datagrams to transmit
QuicConnection.send_stream_data(stream_id, data, end_stream) Queue application data on a QUIC stream
QuicConnection.get_timer() Next deadline for handle_timer() (see Timer Integration)
QuicConnection.handle_timer(now) Process timer expiry (idle timeout, PTO retransmission)
QuicConnection.connect() Client: generate Initial packet with ClientHello
QuicConnection.close() Send CONNECTION_CLOSE and shut down
H3Connection.handle_event(event) Process QUIC events into HTTP/3 events
encode_headers / decode_headers QPACK header compression
pull_quic_header() Parse QUIC packet headers (Initial, Handshake, etc.)
zoomies.recovery Loss detection, RTT estimation, congestion control (RFC 9002)

Key events: HandshakeComplete, StreamDataReceived, ConnectionClosed, NewSessionTicket, ZeroRttAccepted, ZeroRttRejected, PacketDropped, DecryptionFailed


Installation

pip install zoomies

Requires Python 3.14+


Quick Start

QPACK encode/decode

from zoomies.h3 import Header, decode_headers, encode_headers

headers = [
    Header(name=":method", value="GET"),
    Header(name=":path", value="/api/users"),
    Header(name=":scheme", value="https"),
]
encoded = encode_headers(headers)
decoded = decode_headers(encoded)

Parse QUIC Initial packet

from zoomies.encoding import Buffer
from zoomies.packet import pull_quic_header

buf = Buffer(data=raw_bytes)
header = pull_quic_header(buf, host_cid_length=None)
print(f"Version: {header.version:#x}, CID: {header.destination_cid}")

Sans-I/O connection (server)

import time
from zoomies.core import QuicConnection, QuicConfiguration
from zoomies.events import HandshakeComplete

with open("cert.pem", "rb") as f:
    cert = f.read()
with open("key.pem", "rb") as f:
    key = f.read()
config = QuicConfiguration(certificate=cert, private_key=key)
conn = QuicConnection(config)

now = time.monotonic()
events = conn.datagram_received(datagram, addr, now=now)
for event in events:
    if isinstance(event, HandshakeComplete):
        print("Handshake done!")
for dg in conn.send_datagrams(now=now):
    sock.sendto(dg, addr)

Sans-I/O connection (client)

import time
from zoomies.core import QuicConnection, QuicConfiguration

config = QuicConfiguration(is_client=True, verify_mode=False)  # test only — use ca_certs in production
conn = QuicConnection(config)
conn.connect()

now = time.monotonic()
for dg in conn.send_datagrams(now=now):
    sock.sendto(dg, server_addr)

Timer integration (required for production use)

Zoomies is sans-I/O: the library never sleeps. You must drive the timer by polling get_timer() and calling handle_timer() when the deadline passes. Without this, idle timeouts and PTO retransmissions won't fire.

import time

while True:
    now = time.monotonic()
    # 1. Process incoming datagrams
    for dg in receive_from_socket():
        events = conn.datagram_received(dg, addr, now=now)
        handle_events(events)

    # 2. Send outbound datagrams
    for dg in conn.send_datagrams(now=now):
        sock.sendto(dg, addr)

    # 3. Drive the timer — this is what makes idle_timeout and PTO work
    deadline = conn.get_timer()
    if deadline is not None and now >= deadline:
        events = conn.handle_timer(now)
        handle_events(events)

See examples/realistic_server.py for a complete select()-based implementation.

0-RTT early data (TLS resumption)

After a first connection, capture the session ticket and reuse it to send data before the handshake completes on reconnection:

from zoomies.events import NewSessionTicket, ZeroRttAccepted, ZeroRttRejected

# 1. First connection: capture the session ticket
for event in events:
    if isinstance(event, NewSessionTicket):
        stored_ticket = event.ticket  # save externally (file, DB, etc.)

# 2. Reconnect with the ticket
config = QuicConfiguration(is_client=True, session_ticket=stored_ticket, ...)
conn = QuicConnection(config)
conn.connect()

# 3. Queue data immediately (sent as 0-RTT before handshake completes)
conn.send_stream_data(stream_id=0, data=b"early request", end_stream=True)

# 4. Check if the server accepted 0-RTT
for event in events:
    if isinstance(event, ZeroRttAccepted):
        pass  # early data was accepted
    elif isinstance(event, ZeroRttRejected):
        pass  # resend data — it will go as 1-RTT after handshake

See examples/zero_rtt_resumption.py for the full end-to-end flow.

Run the examples (from repo root):

uv run python -m examples.qpack_roundtrip
uv run python -m examples.parse_initial_packet
uv run python -m examples.sans_io_connection
uv run python -m examples.client_server

Examples

Example Description
examples/qpack_roundtrip.py QPACK header encode/decode
examples/parse_initial_packet.py Parse QUIC Initial packet header
examples/sans_io_connection.py Sans-I/O QuicConnection demo (uses test fixtures)
examples/stream_echo.py Stream reassembly, RTT estimation, congestion control, loss detection, PTO timer loop
examples/client_server.py HTTP/3 GET request/response over loopback (client + server in one process)

Usage

Events — Frozen dataclasses for protocol state changes
from zoomies.events import (
    HandshakeComplete,
    StreamDataReceived,
    StreamReset,
    ConnectionClosed,
)

for event in conn.datagram_received(datagram, addr):
    match event:
        case HandshakeComplete():
            ...
        case StreamDataReceived(stream_id=sid, data=data):
            ...
        case StreamReset(stream_id=sid, error_code=code):
            ...
        case ConnectionClosed():
            ...
HTTP/3 — H3Connection for request/response
from zoomies.h3 import H3Connection
from zoomies.events import H3HeadersReceived, H3DataReceived

# Wrap a QuicConnection to add HTTP/3 framing
h3 = H3Connection(sender=quic_conn)

# Client: send request
h3.send_headers(stream_id=0, headers=[
    (b":method", b"GET"), (b":path", b"/"),
    (b":scheme", b"https"), (b":authority", b"localhost"),
], end_stream=True)

# Server: process QUIC events through H3
for quic_event in events:
    for h3_event in h3.handle_event(quic_event):
        match h3_event:
            case H3HeadersReceived(headers=hdrs):
                ...
            case H3DataReceived(data=body):
                ...
Free-threading — Python 3.14t

Zoomies uses frozen dataclasses, no shared mutable state, and cryptography (3.14t-compatible). Safe to run multiple QuicConnection instances from different threads.


Development

git clone https://github.com/lbliii/zoomies.git
cd zoomies
uv sync --group dev
pytest

Lint and types:

ruff check src tests
ty check

Related: The Bengal Ecosystem

Zoomies is developed as part of the b-stack but is standalone. No imports from Bengal, Chirp, or Pounce. A structured reactive stack — every layer written in pure Python for 3.14t free-threading.

ᓚᘏᗢ Bengal Static site generator Docs
∿∿ Purr Content runtime
⌁⌁ Chirp Web framework Docs
=^..^= Pounce ASGI server Docs
)彡 Kida Template engine Docs
ฅᨐฅ Patitas Markdown parser Docs
⌾⌾⌾ Rosettes Syntax highlighter Docs
⟢⟣ Zoomies QUIC/HTTP/3 ← You are here

Python-native. Free-threading ready. No npm required.


License

MIT License — see LICENSE for details.

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

bengal_zoomies-0.3.3.tar.gz (133.7 kB view details)

Uploaded Source

Built Distribution

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

bengal_zoomies-0.3.3-py3-none-any.whl (85.6 kB view details)

Uploaded Python 3

File details

Details for the file bengal_zoomies-0.3.3.tar.gz.

File metadata

  • Download URL: bengal_zoomies-0.3.3.tar.gz
  • Upload date:
  • Size: 133.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for bengal_zoomies-0.3.3.tar.gz
Algorithm Hash digest
SHA256 7ba3184c0fa99bf6da4a1a38de3216054f98f5a79ecb4acd3c254d41d9804c48
MD5 9c1001a2de324516ac81dc9b101ef6f7
BLAKE2b-256 957309f126ee92e1ba9a193c1309f5df01ce3156ce7a8d75a3e385fefeb1e76f

See more details on using hashes here.

Provenance

The following attestation bundles were made for bengal_zoomies-0.3.3.tar.gz:

Publisher: python-publish.yml on lbliii/zoomies

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file bengal_zoomies-0.3.3-py3-none-any.whl.

File metadata

  • Download URL: bengal_zoomies-0.3.3-py3-none-any.whl
  • Upload date:
  • Size: 85.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for bengal_zoomies-0.3.3-py3-none-any.whl
Algorithm Hash digest
SHA256 e99849ec8ef4d0e86461213619a2705b588ac3f5946b880e9e6ae2c1ab9b6e91
MD5 9aaaca611eadee63298c29ce8080bc4f
BLAKE2b-256 297d431f386febf15c0182bd65d71ab873c7f982d6293bdf49ce5afcc7ed5070

See more details on using hashes here.

Provenance

The following attestation bundles were made for bengal_zoomies-0.3.3-py3-none-any.whl:

Publisher: python-publish.yml on lbliii/zoomies

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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