hypothesisphp / tunnels
PHP socket transport library — AES-256-GCM encryption, ephemeral key exchange with forward secrecy, non-blocking I/O via PHP Fibers, session key rotation, heartbeat keep-alive, replay attack protection, and extensible middleware pipeline.
v1.0.0
2026-07-17 02:50 UTC
Requires
- php: >=8.2
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^11.0
This package is not auto-updated.
Last update: 2026-07-18 01:06:33 UTC
README
PHP socket transport library — secure, non-blocking, and extensible.
Built on native stream_socket with PHP 8.2 Fibers, AES-256-GCM encryption, ephemeral key exchange with forward secrecy, replay-attack-resistant handshake, session key rotation, heartbeat keep-alive, and a fluent API designed for long-running CLI processes.
Features
- Non-blocking I/O via PHP 8.2 Fibers — no event loop dependency required
- AES-256-GCM encryption on every frame — authenticated, tamper-proof
- Ephemeral key exchange — fresh random contributions per session → forward secrecy
- Secure handshake with HMAC-SHA256, timestamp window ±30s, and nonce tracking (replay protection)
- Persistent nonce store —
InMemoryNonceStore,FileNonceStore, or customNonceStoreInterface - Session key rotation — time-based, volume-based, or composite policies
- Heartbeat / keep-alive — automatic PING/PONG, idle detection, manual
ping()with RTT measurement - Receive-side frame guard — 16 MB hard limit on incoming frames
- Middleware pipeline —
CompressionMiddleware,PayloadSizeMiddleware,LoggingMiddleware,RateLimitMiddleware(token bucket),HeartbeatMiddleware - Observer/Event system — lifecycle events including
KEY_ROTATED - Fluent builder API — zero boilerplate for common cases
- TLS transport — configurable cert/key/CA via
->withTls() - Zero mandatory dependencies — pure PHP 8.2, no external packages required
- Extensible by design — swap encryption, handshake, logger, nonce store, or middleware via interfaces
- Resilient connections — automatic reconnect with exponential backoff
- Connection pool — multi-peer server management with broadcast support
- Session statistics — bytes/messages sent/received, uptime, last ping
Requirements
- PHP 8.2 or higher
opensslextension (enabled by default in most PHP distributions)zlibextension (only if usingCompressionMiddleware)
Installation
composer require hypothesisphp/tunnels
Quick Start
Server
<?php use HypothesisPHP\Tunnels\TunnelBuilder; use HypothesisPHP\Tunnels\Logging\ConsoleLogger; $tunnel = TunnelBuilder::server('0.0.0.0', 9000) ->withSecret('my-super-secret-key-32bytes!!!!!') ->withLogger(new ConsoleLogger()) ->build(); $tunnel->open(); $message = $tunnel->receive(); echo "Received: {$message}" . PHP_EOL; $tunnel->send('pong'); $tunnel->close();
Client
<?php use HypothesisPHP\Tunnels\TunnelBuilder; use HypothesisPHP\Tunnels\Logging\ConsoleLogger; use HypothesisPHP\Tunnels\Middleware\CompressionMiddleware; use HypothesisPHP\Tunnels\Middleware\PayloadSizeMiddleware; use HypothesisPHP\Tunnels\Middleware\RateLimitMiddleware; use HypothesisPHP\Tunnels\Rotation\CompositeRotationPolicy; use HypothesisPHP\Tunnels\Rotation\TimeBasedRotationPolicy; use HypothesisPHP\Tunnels\Rotation\VolumeBasedRotationPolicy; use HypothesisPHP\Tunnels\Nonce\FileNonceStore; $tunnel = TunnelBuilder::client('127.0.0.1', 9000) ->withSecret('my-super-secret-key-32bytes!!!!!') ->withLogger(new ConsoleLogger()) ->withNonceStore(new FileNonceStore('/var/lib/tunnels')) ->withMiddleware(new PayloadSizeMiddleware(512_000)) ->withMiddleware(new CompressionMiddleware()) ->withMiddleware(new RateLimitMiddleware(capacity: 100, refillPerSecond: 50)) ->withKeyRotation(new CompositeRotationPolicy( new TimeBasedRotationPolicy(3600), new VolumeBasedRotationPolicy(104_857_600), )) ->withHeartbeat(30) ->build(); $tunnel->open(); $tunnel->send('ping'); $response = $tunnel->receive(); echo "Response: {$response}" . PHP_EOL; print_r($tunnel->stats()); $tunnel->close();
One-shot request/reply
$response = TunnelBuilder::client('127.0.0.1', 9000) ->withSecret('my-super-secret-key-32bytes!!!!!') ->build() ->request('hello');
With TLS
$tunnel = TunnelBuilder::client('secure.example.com', 9000) ->withSecret('...') ->withTls( certFile: '/etc/ssl/client.crt', keyFile: '/etc/ssl/client.key', caFile: '/etc/ssl/ca-bundle.crt', verifyPeer: true, ) ->build();
Middleware
$tunnel = TunnelBuilder::client('127.0.0.1', 9000) ->withSecret('...') ->withMiddleware(new PayloadSizeMiddleware(1_048_576)) ->withMiddleware(new CompressionMiddleware(level: 6)) ->withMiddleware(new RateLimitMiddleware(capacity: 100, refillPerSecond: 50)) ->withMiddleware(new LoggingMiddleware($logger)) ->build();
Built-in middleware
| Class | Purpose |
|---|---|
CompressionMiddleware |
gzip compress/decompress payloads |
PayloadSizeMiddleware |
reject oversized frames (send side) |
LoggingMiddleware |
log payload byte counts |
RateLimitMiddleware |
token bucket rate limiter |
HeartbeatMiddleware |
idle PING/PONG keep-alive (auto via ->withHeartbeat()) |
Session Key Rotation
use HypothesisPHP\Tunnels\Rotation\TimeBasedRotationPolicy; $tunnel = TunnelBuilder::client('127.0.0.1', 9000) ->withSecret('...') ->withKeyRotation(new TimeBasedRotationPolicy(3600)) // rotate every hour ->build();
See docs/key-rotation.md for all policies and custom policy guide.
Heartbeat
->withHeartbeat(30) // PING after 30s idle // Manual ping with RTT $rtt = $tunnel->ping(); echo "RTT: {$rtt}ms"; // Liveness check if (!$tunnel->isAlive()) { /* reconnect */ }
Replay Attack Protection
use HypothesisPHP\Tunnels\Nonce\FileNonceStore; // Persistent across restarts — prevents replay after server restart ->withNonceStore(new FileNonceStore('/var/lib/tunnels/nonces'))
Events
use HypothesisPHP\Tunnels\Events\TunnelEvents; $tunnel = TunnelBuilder::client('127.0.0.1', 9000) ->withSecret('...') ->on(TunnelEvents::CONNECTED, new ConnectionLogger()) ->on(TunnelEvents::KEY_ROTATED, new RotationAuditLogger()) ->on(TunnelEvents::ERROR, new AlertNotifier()) ->build();
All events
| Constant | Fired when |
|---|---|
TunnelEvents::CONNECTING |
Before connect attempt |
TunnelEvents::CONNECTED |
Connection + handshake successful |
TunnelEvents::DISCONNECTED |
Transport disconnected (includes stats) |
TunnelEvents::HANDSHAKE_START |
Handshake begins |
TunnelEvents::HANDSHAKE_SUCCESS |
Handshake completed |
TunnelEvents::HANDSHAKE_FAILED |
Handshake rejected |
TunnelEvents::DATA_SENT |
Frame sent |
TunnelEvents::DATA_RECEIVED |
Frame received |
TunnelEvents::KEY_ROTATED |
Session key rotated |
TunnelEvents::ERROR |
Any transport/middleware error |
Security
- AES-256-GCM: authenticated encryption — tampering detected on every frame
- Ephemeral key exchange: fresh random contributions per session → forward secrecy
- HKDF-SHA256: session key derived from ephemeral values + shared secret — never transmitted
- HMAC-SHA256: handshake authentication proves knowledge of shared secret
- Timestamp window ±30s + nonce store: two-layer replay attack prevention
- Receive-side frame guard: 16 MB hard limit prevents memory exhaustion
- Rate limiting: token bucket middleware protects against frame flooding
- TLS: optional transport-layer encryption for public network deployment
Docs
| Document | Topic |
|---|---|
| architecture.md | Design patterns, data flow, Fiber model |
| encryption.md | AES-256-GCM, key derivation, custom drivers |
| handshake.md | Ephemeral key exchange, replay protection |
| security.md | TLS, nonce stores, frame guard, rate limiting |
| middleware.md | Pipeline, built-in middleware, custom middleware |
| events.md | Observer system, all events, async listeners |
| heartbeat.md | Keep-alive, manual ping, session stats |
| key-rotation.md | Rotation policies, custom policy, KEY_ROTATED event |
| connection-pool.md | Multi-peer management, broadcast |
| extending.md | All extension points with examples |
License
MIT — see LICENSE.