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

> Explore the internal components of Hysteria core v2 discover its architecture built on QUIC. Understand server client implementations configuration congestion control and more within the apernet/hysteria repo.

- Repository: [Aperture Internet Laboratory/hysteria](https://github.com/apernet/hysteria)
- Tags: deep-dive
- Published: 2026-05-13

---

**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`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/proxy.go) file defines `WriteTCPRequest()` for serializing connection requests and `UDPMessage` structures for datagram encapsulation. The [`http.go`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/core/server/udp.go) and [`core/client/udp.go`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/core/internal/utils/atomic.go) file provides `AtomicTime` for lock-free timestamp updates, while [`core/internal/utils/qstream.go`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/core/internal/pmtud/avail.go) and [`core/internal/pmtud/unavail.go`](https://github.com/apernet/hysteria/blob/main/core/internal/pmtud/unavail.go) for future MTU probing capabilities.

Standardized error definitions reside in [`core/errors/errors.go`](https://github.com/apernet/hysteria/blob/main/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:

```go
// 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)
	}
}

```

```go
// 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`](https://github.com/apernet/hysteria/blob/main/core/server/server.go), [`core/client/client.go`](https://github.com/apernet/hysteria/blob/main/core/client/client.go)) handle QUIC connection lifecycle and proxy APIs.
- **Configuration** ([`core/server/config.go`](https://github.com/apernet/hysteria/blob/main/core/server/config.go), [`core/client/config.go`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/core/internal/frag/frag.go)) manages large UDP datagram splitting.
- **Utilities** (`core/internal/utils/`) and **Errors** ([`core/errors/errors.go`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/core/client/udp.go)) and server ([`core/server/udp.go`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/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`](https://github.com/apernet/hysteria/blob/main/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.