php-tuic-client is a pure PHP TUIC v5 client focused on one job: start a local socks5://127.0.0.1:1080 style proxy and relay it through a TUIC node.
The QUIC transport is provided by cloudflare/quiche through PHP FFI. The local accept loop, node parsing, CLI flow, and runtime control stay in PHP.
- TUIC v5 authentication
- local SOCKS5 server
- Clash-style YAML / JSON node parsing
- Windows, Linux, and macOS project support
- cross-platform startup scripts
doctorandrunCLI workflow- compatibility request helpers for framework projects, built on top of the same local SOCKS5 runtime
Not in scope right now:
- UDP relay
- HTTP proxy runtime
- multi-node scheduling
- PHP
8.2.4+ ext-ffiext-jsonext-opensslext-sockets- a loadable
libquicheshared library
composer require 18230/php-tuic-clientOfficial release tags vendor prebuilt libquiche binaries for:
windows-x64linux-x64macos-x64
That means a normal composer require from a tagged release can use the bundled shared library directly. If you install from dev-main or run on another architecture, build your own library and point QUICHE_LIB or --quiche-lib at it.
Official releases already include prebuilt x64 libraries. Build manually only if you are developing locally, using dev-main, or targeting another architecture.
You need a shared quiche build with the ffi feature enabled.
Windows:
.\scripts\build-quiche.ps1Linux / macOS:
./scripts/build-quiche.shIf your library lives outside the default search locations, point QUICHE_LIB or --quiche-lib to the absolute path.
Validate runtime prerequisites:
php bin/tuic-client doctor --config=examples/node.example.yamlThe doctor command prints the detected native triplet and the library search order. On official release installs it should resolve the bundled file under resources/native/<platform>-<arch>/.
Start with inline YAML:
php bin/tuic-client \
--node="{ name: 'SG 01', type: tuic, server: your-tuic-server.example.com, port: 443, uuid: 11111111-1111-1111-1111-111111111111, password: your-password, alpn: [h3], disable-sni: false, reduce-rtt: false, udp-relay-mode: native, congestion-controller: bbr, skip-cert-verify: false, sni: your-tuic-server.example.com }" \
--listen=127.0.0.1:1080Or start from a config file:
php bin/tuic-client --config=examples/node.example.yaml --listen=127.0.0.1:1080Then point your tools at:
socks5://127.0.0.1:1080
For command-line tools, prefer socks5h:// so DNS resolution also goes through the proxy:
curl --proxy socks5h://127.0.0.1:1080 https://api.ipify.org?format=jsonExamples:
<?php
$ch = curl_init('https://api.ipify.org?format=json');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_PROXY => '127.0.0.1:1080',
CURLOPT_PROXYTYPE => CURLPROXY_SOCKS5_HOSTNAME,
]);
echo curl_exec($ch);
curl_close($ch);Required TUIC node fields:
name: local display nametype: must betuicserver: remote TUIC server hostname or IPport: remote TUIC portuuid: TUIC user UUIDpassword: TUIC password / tokenalpn: usually[h3]sni: TLS server name
Supported optional fields:
skip-cert-verify: defaults tofalsedisable-sni: defaults tofalsereduce-rtt: defaults tofalseudp-relay-mode: parsed but UDP relay is not implemented yetcongestion-controller: typicallybbr
Minimal example:
proxies:
- name: demo-tuic
type: tuic
server: example.com
port: 443
uuid: 00000000-0000-4000-8000-000000000000
password: replace-with-your-password
alpn: [h3]
sni: example.comMain options:
--node--config--node-name--listen--allow-ip--max-connections--connect-timeout--idle-timeout--handshake-timeout--status-file--status-interval--log-file--pid-file--quiche-lib--dry-run
Production-style example:
php bin/tuic-client \
--config=/opt/php-tuic-client/config/tuic.yaml \
--listen=127.0.0.1:1080 \
--allow-ip=127.0.0.1 \
--max-connections=2048 \
--connect-timeout=12 \
--idle-timeout=600 \
--handshake-timeout=20 \
--status-file=/opt/php-tuic-client/runtime/status.json \
--log-file=/opt/php-tuic-client/runtime/proxy.log \
--pid-file=/opt/php-tuic-client/runtime/proxy.pidproxies:
- name: demo-tuic
type: tuic
server: example.com
port: 443
uuid: 00000000-0000-4000-8000-000000000000
password: replace-with-your-password
alpn: [h3]
disable-sni: false
reduce-rtt: false
udp-relay-mode: native
congestion-controller: bbr
skip-cert-verify: false
sni: example.comWhen you run the proxy in production, these files are the most useful:
log-file: human-readable runtime logstatus-file: JSON snapshot of listener, node, and counterspid-file: current process ID
The status-file includes counters such as:
accepted_connections_totalclosed_connections_totalhandshake_timeouts_totaltuic_auth_success_totaltuic_auth_failure_total
- Keep the SOCKS5 listener on loopback unless you truly need LAN access.
- Run
doctoragainst the exact PHP binary that will host the long-running process. - Official release tags already ship bundled x64 native libraries inside the Composer package.
- Production flags stay intentionally small and explicit:
allow-ip,max-connections,connect-timeout,idle-timeout,handshake-timeout,status-file,log-file, andpid-file. - If you deploy on another architecture, ship your own
libquichewith the same artifact and either place it under the matching triplet directory or setQUICHE_LIB. - Prefer Linux for long-running production workloads.
doctorfails before startup: Runphp bin/tuic-client doctor ...with the exact PHP binary you will use in production.ext-ffiis disabled: Enable FFI inphp.ini, then rerundoctor.libquichecannot be found: Use the tagged release package or pointQUICHE_LIB/--quiche-libat the absolute library path.- The process starts but local requests fail:
Check
log-fileandstatus-filefirst. Iftuic_auth_failure_totalis non-zero, verify node credentials, SNI, and TLS settings. - Windows / Linux / macOS mismatch: Make sure the packaged native library matches the current platform and architecture.
The package still ships TuicRequestClient and framework service providers for Laravel / ThinkPHP. These helpers are convenience layers built on top of the same local SOCKS5 runtime plus cURL. They are optional and not required if you only want the protocol-level local proxy.
More detail:
composer test
php bin/tuic-client --config=examples/node.example.yaml --dry-run
php bin/tuic-client doctor --config=examples/node.example.yamlThe release pipeline also runs a real-node end-to-end probe on ubuntu-22.04, windows-latest, and macos-15-intel using the packaged libquiche binaries and a local socks5://127.0.0.1:11084 listener.