How to Set Up the Hysteria Core Module: Server and Client Configuration Guide

To set up the Hysteria core module, import the server or client packages from github.com/apernet/hysteria/core/v2, populate the Config struct with TLS settings and network parameters, and instantiate the service via NewServer() or NewClient() to embed a high-performance QUIC proxy in your Go application.

The Hysteria core module resides under the core/ directory in the apernet/hysteria repository and provides low-level building blocks for both server and client implementations. Unlike the command-line interface located in app/, this module is deliberately decoupled to allow embedding into existing Go programs without external binary dependencies.

Core Architecture Overview

The core is organized into three primary areas: server handling, client connectivity, and shared protocol utilities.

Component Responsibility Key Types Source Files
Server Listens on QUIC connections, authenticates clients, and proxies TCP/UDP streams Config, NewServer, serverImpl, h3sHandler core/server/config.go, core/server/server.go
Client Connects to servers, performs handshake, and exposes proxy APIs Config, NewClient, clientImpl core/client/config.go, core/client/client.go
Utilities Protocol encoding, congestion control, and stream abstractions utils.QStream, internal/protocol, internal/congestion core/internal/utils/qstream.go, core/internal/protocol/*

Server initialization flow:

  1. Create a server.Config with TLS certificates, a net.PacketConn (UDP socket), and an Authenticator implementation.
  2. Call config.fill() to validate fields and apply defaults for stream windows and idle timeouts.
  3. Invoke NewServer() to convert TLS configurations, build quic.Config, and start the QUIC listener.
  4. The h3sHandler manages incoming connections, authenticates via Authenticate(), and dispatches TCP streams via handleTCPRequest() or UDP datagrams via udpIOImpl.

Client initialization flow:

  1. Populate client.Config with server address, TLS settings, and authentication credentials.
  2. config.verifyAndFill() normalizes defaults and validates parameters.
  3. clientImpl.connect() creates a UDP socket, builds QUIC configurations, and sends an HTTP/3 POST request containing an AuthRequest.
  4. Post-handshake, the client offers TCP() and UDP() methods for proxy connectivity.

Setting Up a Hysteria Server

To initialize a server, provide TLS credentials, a UDP socket, and an authentication mechanism. The following example demonstrates a minimal implementation using a static authenticator:

package main

import (
    "crypto/tls"
    "log"
    "net"

    "github.com/apernet/hysteria/core/v2/server"
)

type staticAuth struct{}

func (a *staticAuth) Authenticate(addr net.Addr, auth string, tx uint64) (bool, string) {
    return true, "user-123"
}

func main() {
    cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
    if err != nil {
        log.Fatalf("TLS load error: %v", err)
    }

    udpConn, err := net.ListenPacket("udp", ":443")
    if err != nil {
        log.Fatalf("UDP bind error: %v", err)
    }

    cfg := &server.Config{
        TLSConfig: server.TLSConfig{
            Certificates: []tls.Certificate{cert},
        },
        Conn:          udpConn,
        Authenticator: &staticAuth{},
    }

    s, err := server.NewServer(cfg)
    if err != nil {
        log.Fatalf("Server init error: %v", err)
    }

    log.Println("Hysteria server listening...")
    if err := s.Serve(); err != nil {
        log.Fatalf("Serve exited: %v", err)
    }
}

Critical configuration requirements:

  • TLSConfig.Certificates must contain at least one valid certificate; the fill() method in core/server/config.go enforces this validation.
  • Conn expects a raw UDP socket; the core wraps this with QUIC semantics internally.
  • The Authenticator interface requires implementing Authenticate(net.Addr, string, uint64) (bool, string) to validate tokens and return unique user identifiers.

Optional Server Hooks

Beyond basic operation, the server supports pluggable interfaces defined in core/server/server.go:

  • Outbound: Route TCP/UDP traffic to custom backends such as SOCKS5 proxies.
  • RequestHook: Inspect or modify the first packet of requests for protocol sniffing.
  • EventLogger / TrafficLogger: Capture connection lifecycle events and per-stream bandwidth metrics; these hooks can enforce limits by returning false to block streams.
  • MasqHandler: Serve regular HTTP responses when clients present no authentication, useful for traffic obfuscation.

Setting Up a Hysteria Client

The client configuration requires server addressing, TLS settings, and authentication credentials. The handshake process negotiates bandwidth limits and UDP support capabilities:

package main

import (
    "crypto/tls"
    "log"
    "net"

    "github.com/apernet/hysteria/core/v2/client"
)

func main() {
    tlsCfg := client.TLSConfig{
        InsecureSkipVerify: true,
    }

    cfg := &client.Config{
        ServerAddr: &net.UDPAddr{IP: net.ParseIP("192.0.2.1"), Port: 443},
        Auth:       "my-secret-token",
        TLSConfig:  tlsCfg,
    }

    c, info, err := client.NewClient(cfg)
    if err != nil {
        log.Fatalf("Client init error: %v", err)
    }
    defer c.Close()

    log.Printf("Handshake succeeded – UDP enabled: %v, Tx limit: %d\n",
        info.UDPEnabled, info.Tx)

    conn, err := c.TCP("example.org:80")
    if err != nil {
        log.Fatalf("TCP error: %v", err)
    }
    defer conn.Close()

    if info.UDPEnabled {
        udp, err := c.UDP()
        if err != nil {
            log.Fatalf("UDP error: %v", err)
        }
        defer udp.Close()
    }
}

Key implementation details from core/client/client.go:

  • ServerAddr must implement net.Addr, typically using *net.UDPAddr.
  • NewClient() returns a Client interface and HandshakeInfo containing UDPEnabled status and transmission bandwidth limits (Tx).
  • After initialization, TCP(addr) opens reliable QUIC streams while UDP() manages datagram sessions via udpSessionManager.

Client Configuration Options

As defined in core/client/config.go, the Config struct supports:

  • BandwidthConfig: Advertise desired send/receive rates to the server for congestion coordination.
  • CongestionConfig: Select BBR or Cubic algorithms via the internal/congestion package.
  • FastOpen: Enable TCP fast-open for reduced latency on subsequent connections.

Extending the Core with Custom Interfaces

The modular design allows deep customization without modifying protocol internals.

Implementing Custom Authentication

The Authenticator interface in core/server/config.go controls access:

type Authenticator interface {
    Authenticate(addr net.Addr, auth string, tx uint64) (bool, string)
}

Return true and a unique user ID to accept connections, or false to reject. The tx parameter indicates the client's claimed upload bandwidth, enabling quota-based authentication.

Adding Traffic Logging

Implement TrafficLogger to monitor per-stream statistics:

type TrafficLogger interface {
    LogTraffic(id string, tx, rx uint64) bool
    LogEvent(name string)
}

Returning false from LogTraffic terminates the connection immediately, enabling real-time bandwidth enforcement and circuit-breaking logic.

Summary

  • The Hysteria core module resides in github.com/apernet/hysteria/core/v2 and operates independently from the CLI application.
  • Server setup requires implementing server.Config with TLS certificates, a UDP PacketConn, and an Authenticator, then calling server.NewServer() defined in core/server/server.go.
  • Client setup involves configuring client.Config with server addresses and credentials, then using client.NewClient() to obtain TCP/UDP proxy interfaces.
  • Key source files include core/server/config.go for validation logic, core/server/server.go for QUIC listener management, and core/client/client.go for handshake implementation.
  • Extension interfaces (Outbound, RequestHook, TrafficLogger) enable custom routing, packet inspection, and bandwidth management without modifying underlying QUIC code in core/internal/protocol.

Frequently Asked Questions

What is the difference between the Hysteria core module and the CLI application?

The core module (core/) provides programmatic Go APIs for embedding Hysteria into applications, while the CLI (app/) offers a standalone binary with configuration file support. The core exposes interfaces like Authenticator and Outbound for customization, whereas the CLI wraps these with command-line flags and YAML parsing.

How do I implement custom authentication when setting up a Hysteria server?

Implement the Authenticator interface defined in core/server/config.go with an Authenticate(net.Addr, string, uint64) method. This receives the client's address, authentication token, and claimed bandwidth. Return true and a user ID to accept the connection, or false to reject. You can also implement TrafficLogger to enforce per-user quotas by returning false in LogTraffic when limits are exceeded.

Can I use self-signed certificates with the Hysteria core client?

Yes. When configuring client.Config, set TLSConfig.InsecureSkipVerify: true to bypass certificate validation, which is useful for testing with self-signed certs. For production, populate TLSConfig.ServerName and the root CA pool to verify against your private certificate authority.

Which congestion control algorithms are available in the Hysteria core module?

According to core/internal/congestion/, the core supports BBR and Cubic congestion controllers. Configure this via client.Config.CongestionConfig or server-side defaults in server.Config. BBR is recommended for high-latency networks, while Cubic provides traditional TCP-friendly behavior.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →