What Are the Internal Components of Hysteria Core? Architecture Deep Dive

The Hysteria core (v2) comprises server and client implementations built on QUIC, supported by shared internal packages for configuration, protocol serialization, congestion control (Brutal/BBR), UDP fragmentation, and utility types, all located in the core/v2 module of the apernet/hysteria repository.

The Hysteria core provides a high-performance proxy implementation over the QUIC protocol. Found in the core/v2 directory of the apernet/hysteria repository, these internal components handle everything from connection authentication to congestion-controlled data transmission, forming a modular architecture that separates transport logic from application concerns.

Server and Client Architecture

The core contains two complementary halves: a server that accepts QUIC connections and forwards traffic, and a client that initiates the QUIC session, authenticates, and proxies TCP/UDP streams. Both implementations reside in their respective packages under core/v2/server and core/v2/client.

The server entry point in core/server/server.go defines NewServer(), which builds a QUIC listener using quic.Transport and returns a serverImpl struct. This struct handles incoming authenticated connections and manages both TCP and UDP proxying through the Outbound interface.

The client implementation in core/client/client.go provides NewClient(), which establishes the QUIC connection, performs HTTP-3 authentication, and exposes TCP() and UDP() APIs for applications. The client negotiates bandwidth limits and UDP capabilities during the initial handshake.

Configuration Management

Configuration structures validate options and expand defaults for both deployment modes. In core/server/config.go, the server configuration defines listening addresses, TLS settings, authentication providers, and outbound handlers. The client configuration in core/client/config.go specifies server addresses, credentials, bandwidth settings, and protocol preferences.

These configuration files handle validation logic and provide sensible defaults, ensuring that both NewServer() and NewClient() receive complete, validated parameters before initializing QUIC transports.

Protocol Serialization Components

The core/internal/protocol package handles Hysteria-specific framing and HTTP-3 handshakes. The proxy.go file defines WriteTCPRequest() for serializing connection requests and UDPMessage structures for datagram encapsulation. The http.go file manages authentication headers through AuthRequestToHeader() and AuthResponseFromHeader(), processing the HTTP-3 POST request to the / endpoint that carries client tokens and advertised receive bandwidth.

These serialization functions ensure compatibility between client and server by standardizing how addresses, session IDs, and payload data transit over QUIC streams and datagrams.

Congestion Control Implementations

Hysteria provides two distinct congestion control algorithms selectable at runtime. The Brutal controller, implemented in core/internal/congestion/brutal/brutal.go, enforces a fixed sending rate regardless of network conditions, maximizing throughput when bandwidth is known and stable. The BBR controller, found in core/internal/congestion/bbr/bbr_sender.go, implements the Bottleneck Bandwidth and Round-trip propagation time algorithm for dynamic adaptation to network conditions.

Helper functions in these packages enable runtime selection based on configuration, allowing administrators to choose between aggressive fixed-rate transmission or responsive congestion avoidance.

UDP Fragmentation and Session Management

Large UDP datagrams undergo fragmentation to fit QUIC's payload constraints. The core/internal/frag/frag.go package splits oversized packets into compatible fragments and reassembles them on the receiving side.

Both client and server maintain udpSessionManager instances (defined in core/server/udp.go and core/client/udp.go) that track active UDP flows. These managers create udpSessionEntry objects per session ID, map them to real UDP sockets, handle idle timeouts, and coordinate with the fragmentation layer when transmitting large responses.

Utility Types and Error Handling

Shared utility types simplify concurrent operations and transport abstraction. The core/internal/utils/atomic.go file provides AtomicTime for lock-free timestamp updates, while core/internal/utils/qstream.go wraps QUIC streams with a QStream type that implements the standard net.Conn interface. Platform-dependent Path-MTU discovery stubs exist in core/internal/pmtud/avail.go and core/internal/pmtud/unavail.go for future MTU probing capabilities.

Standardized error definitions reside in core/errors/errors.go, exporting types such as AuthError, ConnectError, and ClosedError for consistent error handling across the server, client, and protocol layers.

Data Flow and Connection Lifecycle

Understanding the internal components of Hysteria core requires tracing the typical connection flow:

  1. Initialization: NewServer() creates a QUIC listener with TLS configuration, while NewClient() builds packet sockets and TLS/QUIC configs.
  2. Authentication: The client sends an HTTP-3 POST request via protocol.AuthRequestToHeader(), carrying credentials and bandwidth claims. The server responds with UDP permissions and transmission limits parsed by protocol.AuthResponseFromHeader().
  3. TCP Proxying: When applications call clientImpl.TCP(addr), the client writes a protocol.WriteTCPRequest() frame. The server's handleTCPRequest() parses the address, dials the destination through the configured Outbound, and proxies data bidirectionally using copyTwoWay().
  4. UDP Handling: If authentication enables UDP, both sides instantiate udpSessionManager. Incoming datagrams become protocol.UDPMessage objects, with the manager routing them through session entries and applying fragmentation when necessary.

Implementation Example

The following snippets demonstrate server and client initialization using the core components:

// Server implementation using core/v2
package main

import (
	"log"
	"github.com/apernet/hysteria/core/v2"
	"github.com/apernet/hysteria/core/v2/server"
)

func main() {
	cfg := &core.ServerConfig{
		Listen:    ":443",
		TLSConfig: core.DefaultTLSConfig(),
		Authenticator: core.StaticAuth{User: "myuser", Pass: "mypass"},
		Outbound:  core.DirectOutbound{},
	}
	srv, err := server.NewServer(cfg)
	if err != nil {
		log.Fatalf("server init: %v", err)
	}
	log.Println("Hysteria server listening")
	if err = srv.Serve(); err != nil {
		log.Fatalf("serve: %v", err)
	}
}
// Client implementation using core/v2
package main

import (
	"fmt"
	"log"
	"github.com/apernet/hysteria/core/v2/client"
)

func main() {
	cfg := &core.ClientConfig{
		ServerAddr:   "example.com:443",
		Auth:         "myuser:mypass",
		BandwidthConfig: core.BandwidthConfig{MaxRx: 500 << 20, MaxTx: 500 << 20},
	}
	cli, info, err := client.NewClient(cfg)
	if err != nil {
		log.Fatalf("client init: %v", err)
	}
	fmt.Printf("Handshake: UDP=%v, Tx=%d\n", info.UDPEnabled, info.Tx)

	// TCP proxy example
	conn, err := cli.TCP("google.com:443")
	if err != nil {
		log.Fatalf("tcp: %v", err)
	}
	defer conn.Close()

	// UDP proxy example (if enabled)
	if info.UDPEnabled {
		udp, _ := cli.UDP()
		udp.Send([]byte("ping"), "8.8.8.8:53")
	}
}

Summary

The internal components of Hysteria core organize functionality into distinct, testable layers:

Frequently Asked Questions

What is the difference between Hysteria's Brutal and BBR congestion controllers?

Brutal, implemented in core/internal/congestion/brutal/brutal.go, maintains a fixed sending rate based on configured bandwidth limits, ideal for stable networks where maximum throughput is desired. BBR, found in core/internal/congestion/bbr/bbr_sender.go, dynamically adjusts transmission rates based on actual network capacity and latency measurements, providing better performance over variable or congested links.

How does Hysteria handle large UDP datagrams that exceed QUIC payload limits?

The core/internal/frag/frag.go package implements fragmentation logic that splits oversized UDP datagrams into QUIC-compatible fragments before transmission. The receiving side reassembles these fragments before delivering them to the application, with the udpSessionManager in both client (core/client/udp.go) and server (core/server/udp.go) coordinating this process per session ID.

What authentication mechanism does the Hysteria core use?

The core uses HTTP-3 POST requests to the / (hysteria) endpoint, as defined in core/internal/protocol/http.go. The client sends authentication tokens and advertised receive bandwidth via protocol.AuthRequestToHeader(). The server validates credentials and responds with permissions including UDP availability and bandwidth limits, parsed by protocol.AuthResponseFromHeader().

Where are error types defined in the Hysteria core?

Centralized error definitions reside in core/errors/errors.go, which exports specific error types including AuthError for authentication failures, ConnectError for connection establishment issues, and ClosedError for terminated sessions. These types provide consistent error handling across the server, client, and protocol packages.

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 →