Skip to content

zoptia/quiche-zig

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

quiche-zig

Two things in one repo:

  1. **A precompiled C distribution of Cloudflare quiche
    • BoringSSL** for eight platforms — every GitHub release ships ready-to-link libquiche.a / libssl.a / libcrypto.a (or quiche.lib / ssl.lib / crypto.lib on Windows) plus their public headers. No Rust, CMake, NASM, or Apple /Android SDK required on the consumer side. You can use this directly from C, C++, Go, Swift, or any other language with FFI — see Use from C/C++ below.
  2. A Zig 0.16 wrapper that lazily fetches the matching tarball from the release, links it, and exposes two independent modules:
    • @import("quiche") — RAII wrappers around quiche's QUIC/HTTP3 FFI; raw C bindings remain available under quiche.c.
    • @import("boringssl") — raw BoringSSL bindings (<openssl/ssl.h> and everything it transitively pulls in) under boringssl.c. Useful when you want TLS or crypto without the rest of quiche.

Either way, end users only need a working toolchain — Zig 0.16 for Zig consumers, or any C/C++ compiler for direct C consumers. The prebuilt artifacts are produced fresh by prebuilt.yml on each tag push.

Status

Target Prebuilt artifact Zig build/test in CI
linux-x86_64 ✅ build + test + handshake smoke
linux-aarch64 ✅ build + test + handshake smoke
macos-arm64 ✅ build + test + handshake smoke
ios-arm64 ⚠️ not in CI (cross-target, no runner)
windows-x86_64 ⚠️ blocked on a current zig-master addTranslateC bug with the bundled clang's avx512 headers
windows-arm64 ⚠️ same as above
android-arm64 ⚠️ not in CI (cross-target, no runner)
android-x86_64 ⚠️ not in CI (cross-target, no runner)

The prebuilt column tracks prebuilt-v0.28.0 — every artifact is produced fresh by the prebuilt.yml workflow on each tag push. The CI column tracks what ci.yml actually exercises on every commit.

Heads up on the upstream version: cloudflare/quiche stopped writing release notes after 0.24.5, so their Releases page looks stuck in mid-2025 even though newer tags exist. The actual current quiche crate version lives on the Tags page — that's what QUICHE_VERSION in prebuilt.yml tracks, so don't be surprised if our pin looks ahead of their Releases page.

Tarball layout

Each quiche-<platform>.tar.gz in a release expands to:

quiche-<platform>/
├── lib/
│   ├── libquiche.a     (or quiche.lib on Windows MSVC)
│   ├── libssl.a        (or ssl.lib)
│   └── libcrypto.a     (or crypto.lib)
└── include/
    ├── quiche.h
    └── openssl/        (BoringSSL public headers — ssl.h, crypto.h,
                         evp.h, x509.h, …)

The tarballs are self-contained: BoringSSL is statically linked into both libssl.a and libcrypto.a, and libquiche.a already pulls those in as dependencies. You only need to add the OS-level system libraries listed below when linking.

Use from C/C++

Pick the tarball that matches your target from the latest release:

https://github.com/zoptia/quiche-zig/releases/latest

Verify it against the SHA256SUMS asset, extract, then point your build system at lib/ and include/. Minimal example with plain cc:

TARBALL=quiche-linux-x86_64.tar.gz
curl -fsSL -O "https://github.com/zoptia/quiche-zig/releases/download/prebuilt-v0.28.0/${TARBALL}"
curl -fsSL  "https://github.com/zoptia/quiche-zig/releases/download/prebuilt-v0.28.0/SHA256SUMS" \
  | grep "${TARBALL}" | sha256sum -c -
tar -xzf "${TARBALL}"

cc -o myapp main.c \
   -I quiche-linux-x86_64/include \
   quiche-linux-x86_64/lib/libquiche.a \
   quiche-linux-x86_64/lib/libssl.a \
   quiche-linux-x86_64/lib/libcrypto.a \
   -lpthread -ldl -lm -lrt -lutil -lgcc_s

Per-platform link line for the system libraries:

Platform System libraries
linux -lpthread -ldl -lm -lrt -lutil -lgcc_s
macos / ios -framework Security -framework CoreFoundation -framework SystemConfiguration
windows-msvc ws2_32.lib userenv.lib ntdll.lib bcrypt.lib crypt32.lib secur32.lib advapi32.lib
android -lc (NDK supplies the rest)

You can #include <quiche.h> for the QUIC/H3 API and #include <openssl/ssl.h> (et al.) for the BoringSSL API from the same translation unit.

Use from Zig

Install

zig fetch --save git+https://github.com/zoptia/quiche-zig

Then in your build.zig:

const quiche_dep = b.dependency("quiche_zig", .{
    .target = target,
    .optimize = optimize,
});

const exe = b.addExecutable(.{
    .name = "myapp",
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
        .imports = &.{
            .{ .name = "quiche",    .module = quiche_dep.module("quiche") },
            // Optional — only needed if you call BoringSSL directly.
            .{ .name = "boringssl", .module = quiche_dep.module("boringssl") },
        },
    }),
});
b.installArtifact(exe);

Use

const std = @import("std");
const quiche = @import("quiche");

pub fn main() !void {
    var cfg = try quiche.Config.init(quiche.PROTOCOL_VERSION);
    defer cfg.deinit();
    cfg.verifyPeer(false);
    try cfg.setApplicationProtos(quiche.H3_APPLICATION_PROTOCOL);
    cfg.setMaxIdleTimeout(5_000);
    // … additional transport-parameter setters live as setXxx methods.

    std.debug.print("quiche version: {s}\n", .{quiche.version()});
}

Config, Connection, H3Config, and H3Connection are RAII handles around the corresponding quiche_* C types. Errors translate from the QUICHE_ERR_* codes into the quiche.Error set. When the wrapper isn't expressive enough, quiche.c re-exports the raw @cImported bindings so you can drop down to the C API for anything we don't cover.

See examples/client.zig for a complete client that performs a QUIC handshake against cloudflare-quic.com:443 and prints the negotiated ALPN, and examples/server.zig for a minimal echo server.

For raw BoringSSL from Zig:

const std = @import("std");
const ssl = @import("boringssl");

pub fn main() !void {
    _ = ssl.c.OPENSSL_init_ssl(0, null);
    const ctx = ssl.c.SSL_CTX_new(ssl.c.TLS_method())
        orelse return error.SslCtxNewFailed;
    defer ssl.c.SSL_CTX_free(ctx);
    std.debug.print("OpenSSL version: 0x{x}\n", .{ssl.c.OPENSSL_VERSION_NUMBER});
}

Escape hatch: building against a local quiche

If your target isn't in the matrix, or you want to test against your own fork of quiche, build quiche with cargo build --release --features ffi and pass the resulting prefix to zig build:

zig build -Dquiche-prefix=/path/to/your/quiche-out

quiche-prefix should be a directory with the layout

prefix/
├── lib/
│   ├── libquiche.a   (or quiche.lib on Windows)
│   ├── libssl.a
│   └── libcrypto.a
└── include/
    ├── quiche.h
    └── openssl/      (BoringSSL public headers; only needed if you
                       import the boringssl module)

When -Dquiche-prefix is set, the lazy dependency download is skipped entirely, so you can develop offline.

Upgrading the bundled quiche version

  1. Update QUICHE_VERSION at the top of .github/workflows/prebuilt.yml.
  2. Bump the prebuilt-v<VER> references in build.zig.zon and .github/workflows/ci.yml (search for prebuilt-v0.).
  3. Push a tag named prebuilt-v<VER>. The prebuilt.yml workflow produces fresh per-platform tarballs and creates a GitHub release.
  4. Run zig fetch --save=quiche_linux_x86_64 https://github.com/zoptia/quiche-zig/releases/download/prebuilt-v<VER>/quiche-linux-x86_64.tar.gz (and the equivalent for the other seven platforms) to refresh the hashes in build.zig.zon. Commit the result.

License

This wrapper repo is BSD-2-Clause, matching upstream quiche.

The prebuilt tarballs distribute three things together:

If you redistribute a binary that links any of the prebuilt artifacts, you inherit the corresponding attribution requirements. The bundled LICENSE file in this repo carries the high-level pointers.

About

quiche-zig

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages