# Knafeh

A high-performance QUIC-based RPC framework written in Rust with first-class Python bindings.

Knafeh provides two transport backends — raw QUIC (quinn) for maximum throughput and HTTP/3 (tokio-quiche) for gRPC compatibility — with pluggable codecs, middleware, server-streaming, and connection pooling.

## Features

- **Dual transport** — Raw QUIC (~13K rpc/s) or HTTP/3 (~4K rpc/s), selectable per use case
- **Pluggable codecs** — Protobuf (default), JSON, or implement the `Codec` trait
- **Unary & server-streaming RPCs** — with length-prefixed framing for HTTP/3 streams
- **Middleware** — `Interceptor` trait for auth, logging, tracing, rate limiting
- **Connection pooling** — QUIC-native multiplexing with automatic reconnection
- **Retries** — `RetryPolicy` with exponential backoff, jitter, status-code awareness
- **Python bindings** — Native async (pyo3-async-runtimes) + sync API, anyio structured concurrency
- **gRPC-compatible status codes** — 17 standard codes with proper error propagation

## Quick Start

### Prerequisites

- Rust 1.94+
- Python >= 3.13 (for Python bindings)
- [maturin](https://github.com/PyO3/maturin) (for building Python wheels)

### Generate TLS Certificates

QUIC requires TLS. For development, generate a self-signed cert:

```bash
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
    -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost'
```

Tests generate certs automatically via `rcgen` — no manual setup needed.

### Rust — HTTP/3 Server + Client

```rust
use knafeh::server::Server;
use knafeh::codec::JsonCodec;
use knafeh::transport::tls::TlsConfig;

let server = Server::builder()
    .bind_str("0.0.0.0:4433")?
    .tls(TlsConfig::server("cert.pem", "key.pem"))
    .codec(JsonCodec::new())
    .add_service(my_service)
    .build()?;

server.serve().await?;
```

```rust
use knafeh::client::Client;
use knafeh::codec::JsonCodec;

let client = Client::builder()
    .endpoint("localhost:4433")
    .codec(JsonCodec::new())
    .build()
    .await?;

let response = client.call("greeter/say_hello", body).await?;
```

### Rust — Raw QUIC (maximum performance)

```rust
use knafeh::transport::quic_native::{QuicClient, QuicServer};

// Server
let server = QuicServer::bind(addr, &cert_pem, &key_pem, router, codec, middleware)?;
server.serve().await?;

// Client
let client = QuicClient::connect_insecure(addr, codec, middleware).await?;
let response = client.call("echo/echo", body).await?;
```

### Python — Async Client

```python
from knafeh import Client, TlsConfig

async with Client("localhost:4433", tls=TlsConfig.client_insecure()) as client:
    response = await client.call("greeter/say_hello", b'{"name": "World"}')

    # Batch concurrent calls with structured cancellation (anyio TaskGroup)
    responses = await client.call_many("echo/echo", [b'msg1', b'msg2', b'msg3'])
```

### Python — Sync Client (scripts, notebooks)

```python
from knafeh import SyncClient

with SyncClient("localhost:4433") as client:
    response = client.call("echo/echo", b"hello")
    for chunk in client.server_stream("count/count", b'{"count": 5}'):
        print(chunk)
```

### Python — Server

```python
from knafeh import Server, TlsConfig

server = Server("0.0.0.0:4433", tls=TlsConfig.server("cert.pem", "key.pem"))

@server.service("greeter")
class Greeter:
    def say_hello(self, request: bytes) -> bytes:
        import json
        name = json.loads(request)["name"]
        return json.dumps({"message": f"Hello, {name}!"}).encode()

await server.serve()
```

## Building & Testing

```bash
# Rust crate (Python bindings are opt-in via the python feature)
cargo build
cargo test

# Python
uv sync --python 3.13 --group dev
PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 uv run maturin develop --features python
uv run pytest tests/test_knafeh.py -v

# Performance benchmarks (release mode)
cargo test --no-default-features --release --test e2e_perf_test -- --ignored --nocapture
```

TLS certificates are generated at runtime via `rcgen` — no manual cert setup needed for tests.

## Releasing

Releases are published by the GitHub Actions release workflow when a semver tag like `v1.0.0` is pushed. Before tagging, update the matching versions in `Cargo.toml` and `pyproject.toml`. For an existing tag, run the workflow manually and provide the tag value.

Required repository secrets:

- `CARGO_REGISTRY_TOKEN` for crates.io
- `PYPI_TOKEN` for PyPI

The workflow publishes the Rust crate and Linux x86_64 PyPI wheels for Python 3.13 and 3.14.

## Performance

Benchmarked on localhost, release mode, 10K sequential requests:

| Transport | Throughput | p50 Latency |
|-----------|-----------|-------------|
| Raw QUIC (quinn) | **12,680 rpc/s** | **76 us** |
| HTTP/3 (tokio-quiche) | 4,300 rpc/s | 230 us |

Parallel (32 in-flight): Raw QUIC reaches **104K rpc/s**, HTTP/3 reaches **15K rpc/s**.

See [`docs/E2E_TESTING_REPORT_v1.0.0.md`](docs/E2E_TESTING_REPORT_v1.0.0.md) for full benchmark results including codec comparisons, Python client/server benchmarks, and external framework comparisons.

## License

Apache-2.0
