TrojanServer
TrojanServer is the server-side Trojan protocol tunnel for WaterWall. It parses a Trojan byte stream from the previous
node, authenticates the standard SHA224 password hash, extracts the requested TCP or UDP destination, and forwards
accepted traffic to its configured next node.
The node supports local password allowlists and AuthenticationClient backed users. It also supports optional fallback
for unauthenticated invalid probes.
This current node replaces the older two-node setup that used separate TrojanAuthServer and TrojanSocksServer nodes.
Typical Placement
Normal Trojan server:
TcpListener -> TlsServer -> TrojanServer -> TcpUdpConnector
TCP-only outbound:
TcpListener -> TlsServer -> TrojanServer -> TcpConnector
With routing after authentication:
TcpListener -> TlsServer -> TrojanServer -> Router
TrojanServer does not terminate TLS by itself. For public Trojan deployments, place TlsServer before it. Without TLS,
the password hash and destination metadata are visible on the wire.
Minimal Local-Password Example
[
{
"name": "tls-in",
"type": "TlsServer",
"settings": {
"cert-file": "/etc/waterwall/fullchain.pem",
"key-file": "/etc/waterwall/privkey.pem"
},
"next": "trojan-server"
},
{
"name": "trojan-server",
"type": "TrojanServer",
"settings": {
"password": "secret-password",
"connect": true,
"udp": true,
"verbose": false
},
"next": "outbound"
},
{
"name": "outbound",
"type": "TcpUdpConnector",
"settings": {
"address": "dest_context->address",
"port": "dest_context->port"
}
}
]
Multiple Local Users
{
"name": "trojan-server",
"type": "TrojanServer",
"settings": {
"users": [
"secret-password",
{
"username": "alice",
"password": "another-secret"
}
],
"connect": true,
"udp": true
},
"next": "outbound"
}
In local allowlist mode, TrojanServer stores the matched raw password on the line so Router can match password rules.
If the matching object has username, that username is also stored on the line for username-aware routing.
AuthenticationClient Mode
{
"name": "trojan-server",
"type": "TrojanServer",
"settings": {
"auth-client-node-name": "auth-client",
"connect": true,
"udp": true,
"verbose": false
},
"next": "outbound"
}
In this mode, the configured AuthenticationClient is the authority. Store each user's plaintext Trojan password in the
user object's password field; AuthenticationClient prepares the SHA224 lookup data internally. The user object's
name is an account name, not the Trojan password itself.
When auth-client-node-name is configured, local password settings are rejected. TrojanServer internally inserts a
UserController before the configured outbound node, so do not place a manual UserController directly after it.
Fallback Example
{
"name": "trojan-server",
"type": "TrojanServer",
"settings": {
"password": "secret-password",
"fallback-node-name": "fallback-service",
"fallback-intentional-delay-ms": 7,
"fallback-intentional-delay-jitter-ms": 1,
"connect": true,
"udp": true
},
"next": "outbound"
}
Fallback is for active-probing resistance. Invalid unauthenticated traffic can be handed to another configured node instead of being closed immediately.
Required Fields
Top-level fields:
| Field | Type | Description |
|---|---|---|
name | string | User-chosen node name. Must be unique inside the config file. |
type | string | Must be exactly "TrojanServer". |
settings | object | Non-empty Trojan server settings. |
next | string | Required. Accepted Trojan traffic is forwarded to this node. |
Choose exactly one authentication mode.
Local Authentication Settings
Use local authentication by omitting auth-client-node-name and configuring at least one raw password.
| Field | Type | Description |
|---|---|---|
password | string | One raw Trojan password. |
pass | string | Alias for password. Use either password or pass, not both. |
passwords | array of strings | Multiple raw Trojan passwords. |
users | array | Password strings or objects with password or pass. Objects can include username. |
clients | array | Alias for users. Use either users or clients, not both. |
Password strings must be non-empty. Duplicate local passwords are fatal configuration errors, even when the duplicate entries use different usernames.
Database Authentication Settings
| Field | Type | Description |
|---|---|---|
auth-client-node-name | string | Name of an existing AuthenticationClient node in the same config file. |
The referenced node must exist, must be an AuthenticationClient, and must not point back to the TrojanServer node
itself. In database mode, do not configure password, pass, passwords, users, or clients.
Optional Settings
| Field | Default | Description |
|---|---|---|
connect | true | Enables Trojan TCP CONNECT, command 0x01. |
udp | true | Enables Trojan UDP ASSOCIATE, command 0x03. |
verbose | false | Enables focused authentication diagnostics. |
fallback-node-name | not set | Fallback branch for unauthenticated invalid probes. Aliases: fallback-node, fallback. |
fallback-intentional-delay-ms | 7 | Delay for upstream payloads sent to fallback. Set 0 to disable. Must not be negative. |
fallback-intentional-delay-jitter-ms | 1 | Random jitter around delayed fallback payload scheduling. Ignored when delay is 0. Must not be negative. |
sweep-interval-ms | 1000 | Optional setting forwarded to the internally created UserController in database mode. |
At least one of connect or udp must be enabled.
Request Format
Accepted Trojan request:
password: 56 ASCII hex bytes, hex(SHA224(password))
separator: CRLF
command: 01 CONNECT or 03 UDP ASSOCIATE
destination: ATYP + address + port
separator: CRLF
body: TCP stream bytes or Trojan UDP packets
Destination format:
ATYP 01: IPv4 address, 4 bytes, then 2-byte big-endian port
ATYP 03: domain length, domain bytes, then 2-byte big-endian port
ATYP 04: IPv6 address, 16 bytes, then 2-byte big-endian port
Trojan UDP packet format:
destination: ATYP + address + port
length: 2 bytes big-endian
separator: CRLF
payload: length bytes
Authentication Flow
Trojan clients send hex(SHA224(password)) as 56 ASCII hex bytes. TrojanServer decodes that value and authenticates it
against the selected mode.
In local mode, the server precomputes SHA224 for each configured raw password. In database mode, it uses the
AuthenticationClient SHA224 lookup API and records the returned user handle on the line after the request is parsed.
The complete 56-byte password hash must be present in the first upstream payload callback. If the first payload does not contain the full hash, the line is treated as an invalid unauthenticated probe. With fallback configured, those bytes go to fallback; otherwise the line is closed. Later request fields, including the CRLF, command, destination, and early body bytes, may still arrive across later payload callbacks after the complete password hash is present.
There is no unauthenticated Trojan mode.
TCP CONNECT Runtime Behavior
For command 0x01, the server:
- authenticates the password hash
- parses the requested destination
- writes that destination into
line->routing_context.dest_ctx - records local or database credentials on the line for downstream routing
- initializes the configured
nextnode - forwards any TCP body bytes that arrived in the same payload as the request header
- forwards downstream
Estonly after the selected outbound path establishes
The configured next node is usually TcpConnector, TcpUdpConnector, Router, or another node that understands the
destination context.
UDP ASSOCIATE Runtime Behavior
For command 0x03, the initial request address is only the association metadata. The real remote destination is carried
by each Trojan UDP packet.
For each unique UDP packet destination, TrojanServer creates or reuses one internal backend UDP line. That backend line
gets the packet destination in line->routing_context.dest_ctx, carries the authenticated user credentials, and forwards
the raw UDP payload to the configured next node.
Replies from backend UDP lines are wrapped back into Trojan UDP packet framing and sent downstream to the original Trojan client stream. One Trojan UDP association can therefore talk to multiple remote UDP endpoints.
Malformed UDP packet headers, zero-length domain names, missing ports, bad CRLF separators, or UDP payloads larger than 8192 bytes close the affected Trojan line. Pending downstream reply data is capped at 1 MiB.
Fallback Behavior
Fallback is used only before successful password authentication. After authentication succeeds, malformed Trojan protocol data closes the line instead of being replayed to fallback.
Fallback may be selected for:
- invalid password prefix bytes
- a password hash split across the first upstream payload
- invalid password CRLF before authentication
- invalid password hex before authentication
- failed password lookup
AuthenticationClientnot being ready- an incomplete unauthenticated prefix that grows beyond the initial-buffer limit
When fallback is selected:
- the fallback branch receives
Initimmediately - saved initial bytes and later upstream payloads are forwarded to fallback
- upstream fallback payloads can be delayed by
fallback-intentional-delay-msplus jitter - an upstream
Finishwaits until delayed fallback payloads have been delivered - downstream fallback responses are not intentionally delayed
The default fallback delay is intentionally small. Tune delay and jitter against the public behavior the fallback is supposed to resemble; they are mitigations, not proof of timing indistinguishability.
Finish Behavior
TrojanServer destroys its own line state before propagating real Finish callbacks.
The client stream line is created by the previous adapter and is not destroyed by TrojanServer. Internal UDP backend
lines are created by TrojanServer; it owns those lines and destroys them safely when the client stream closes or when a
backend UDP line closes.
Padding
TrojanServer may prepend the largest possible Trojan UDP reply header:
required_padding_left = 263
That covers ATYP + domain length + 255-byte domain + port + payload length + CRLF.
Node Metadata
| Property | Value |
|---|---|
| Node flag | kNodeFlagChainHead |
| Previous node | Allowed, required in normal use |
| Next node | Required |
| Layer group | kNodeLayerAnything |
required_padding_left | 263 |
| Line state | authentication state, phase, read stream, pending queues, fallback state, UDP backend-line map |
Unsupported Features
This implementation does not create or manage:
- TLS termination
- WebSocket, HTTP, gRPC, or other transport wrappers
- legacy
TrojanAuthServerplusTrojanSocksServercomposition - non-standard Trojan extensions
Use separate WaterWall nodes such as TlsServer, HttpServer, MuxServer, TcpUdpConnector, or Router when you need
those layers.
Common Mistakes
- Do not expose
TrojanServerdirectly on a public TCP listener withoutTlsServerunless you intentionally want plain Trojan on the wire. - Do not configure local passwords together with
auth-client-node-name. - Do not set the next node to a manual
UserController; database mode inserts its own internalUserController. - Do not use the old
TrojanAuthServerandTrojanSocksServernode names for new configurations. - Do not expect the initial UDP ASSOCIATE address to choose the final UDP target; each Trojan UDP packet carries its own target address.