Dynamic User Management via API
Note: Trojan does not support this feature
Trojan-Go provides a set of APIs via gRPC. The API supports the following features:
-
Add, delete, modify, and query user information
-
Traffic statistics
-
Speed statistics
-
IP connection count statistics
Trojan-Go itself has integrated API control functionality, meaning you can use one Trojan-Go instance to manage another Trojan-Go server.
You need to add API settings to the server configuration you want to control, for example:
{
...
"api": {
"enabled": true,
"api_addr": "127.0.0.1",
"api_port": 10000,
"allow_payload_capture": false
}
}
allow_payload_capture defaults to false and should be left at false for production deployments. It is only honoured by binaries built with the apidebug build tag; default release builds silently downgrade GetRecords to metadata-only streaming regardless of the request’s IncludePayload flag, so production scripts cannot accidentally leak connection bytes. With the apidebug tag and the flag set to false, GetRecords returns PermissionDenied so the misconfiguration is loud rather than silent.
When api_addr is bound to a non-loopback interface, trojan-go logs a WARN at startup if TLS is not enabled, and a separate WARN if TLS is enabled but verify_client is off. The server is not refused so existing private-network deployments keep working, but the operator should treat such a setup as world-readable and mTLS-protect any internet-reachable API endpoint.
Then start the Trojan-Go server:
./trojan-go -config ./server.json
Then you can use another Trojan-Go to connect to the server for management. The basic command format is:
./trojan-go -api-addr SERVER_API_ADDRESS -api COMMAND
Where SERVER_API_ADDRESS is the API address and port, such as 127.0.0.1:10000.
COMMAND is the API command. Valid commands are:
-
list — list all users
-
get — get information for a specific user
-
set — set user information (add / delete / modify)
Below are some examples:
-
List all user information
./trojan-go -api-addr 127.0.0.1:10000 -api listAll user information will be exported in JSON format, including the number of online IPs, real-time speed, total upload and download traffic, etc. Below is an example of a returned result:
[{"user":{"hash":"d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01"},"status":{"traffic_total":{"upload_traffic":36393,"download_traffic":186478},"speed_current":{"upload_speed":25210,"download_speed":72384},"speed_limit":{"upload_speed":5242880,"download_speed":5242880},"ip_limit":50,"quota":10737418240}}]Traffic units are all bytes.
quotais the traffic quota in bytes (negative = unlimited, 0 = disabled, positive = byte limit). -
Get a user’s information
You can use
-target-passwordto specify a password, or-target-hashto specify the SHA224 hash of the target user’s password. The format is the same as the list command:./trojan-go -api-addr 127.0.0.1:10000 -api get -target-password passwordOr:
./trojan-go -api-addr 127.0.0.1:10000 -api get -target-hash d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01The above two commands are equivalent. The following examples uniformly use the plaintext password method; using a hash to specify a user follows the same logic.
The user’s information will be exported in JSON format, similar to the list command. Below is an example of a returned result:
{"user":{"hash":"d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01"},"status":{"traffic_total":{"upload_traffic":36393,"download_traffic":186478},"speed_current":{"upload_speed":25210,"download_speed":72384},"speed_limit":{"upload_speed":5242880,"download_speed":5242880},"ip_limit":50,"quota":10737418240}} -
Add a user
./trojan-go -api-addr 127.0.0.1:10000 -api set -add-profile -target-password password -
Delete a user
./trojan-go -api-addr 127.0.0.1:10000 -api set -delete-profile -target-password password -
Modify a user’s information
./trojan-go -api-addr 127.0.0.1:10000 -api set -modify-profile -target-password password \ -ip-limit 3 \ -upload-speed-limit 5242880 \ -download-speed-limit 5242880 \ -quota 10737418240This command limits the upload and download speed of the user with password “password” to 5 MiB/s, limits the number of simultaneously connected IPs to 3, and sets the traffic quota to 10 GiB (10737418240 bytes). Note that speed values are in bytes per second. If 0 or a negative number is entered for speed or IP limit, it means no limit.
The CLI distinguishes between “flag omitted” and “flag explicitly set to its zero value”. Only flags that you actually pass on the command line are sent in the request — fields you do not mention are left untouched on the server side. In particular,
trojan-go -api set -modify-profile -target-password password -ip-limit 3does not zero out the user’s existing quota; thequotafield is simply not included in the request. -
Manage a user’s quota
Quota can be read from the
quotafield in thegetorlistresponse. To set or modify a user’s quota via the CLI:./trojan-go -api-addr 127.0.0.1:10000 -api set -modify-profile -target-password password -quota 10737418240This sets the quota for the user with password “password” to 10 GiB (10737418240 bytes).
Quota semantics:
- Negative value (typically
-1) — unlimited. Newly created users start at-1so a staticpasswordlist in the YAML/JSON config keeps authenticating. 0— user is disabled. Authentication is rejected and any active tunnel is closed via the per-user cutoff signal.- Positive value — byte limit. Once
download + upload >= quotathe user’s active tunnel is closed in-flight (within ~1 second of crossing the threshold) rather than at the next polling tick.
On the wire, the proto3
quotafield isoptional int64, so callers can distinguish “field absent” (leave existing value as is) from “field present with value 0” (explicitly disable the user). Add with omitted quota leaves the user at the default-1; Add with explicitquota=0disables the user; Modify with omitted quota preserves the previous value; Modify with explicitquota=0disables the user. The CLI usesflag.Visitto enforce the same convention —-quotais only sent when you pass it on the command line. Downstream Go gRPC consumers should regenerate their stubs:UserStatus.Quotais now*int64with the standardGetQuota()accessor. - Negative value (typically