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:
- Initialization:
NewServer()creates a QUIC listener with TLS configuration, whileNewClient()builds packet sockets and TLS/QUIC configs. - 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 byprotocol.AuthResponseFromHeader(). - TCP Proxying: When applications call
clientImpl.TCP(addr), the client writes aprotocol.WriteTCPRequest()frame. The server'shandleTCPRequest()parses the address, dials the destination through the configuredOutbound, and proxies data bidirectionally usingcopyTwoWay(). - UDP Handling: If authentication enables UDP, both sides instantiate
udpSessionManager. Incoming datagrams becomeprotocol.UDPMessageobjects, 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:
- Server and Client implementations (
core/server/server.go,core/client/client.go) handle QUIC connection lifecycle and proxy APIs. - Configuration (
core/server/config.go,core/client/config.go) validates deployment-specific options. - Protocol utilities (
core/internal/protocol/) serialize frames and authentication headers. - Congestion control (
core/internal/congestion/) provides Brutal and BBR algorithms. - Fragmentation (
core/internal/frag/frag.go) manages large UDP datagram splitting. - Utilities (
core/internal/utils/) and Errors (core/errors/errors.go) provide shared infrastructure types.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →