laylink/contract.md
2026-05-28 20:19:28 +08:00

27 KiB

POP-Controlled Reverse Access Gateway Contract

Implementation Status

Last updated: 2026-05-28 Asia/Shanghai.

Current phase: MVP bootstrap in progress.

Direction update under evaluation:

  • Legacy agent naming has been migrated to Client Agent for the first MVP path.
  • First capability being implemented:
    • Client connects to Client Agent.
    • Client Agent connects outbound to POP Server.
    • Client Agent wraps all traffic into LayLink frames.
    • POP Server authenticates/authorizes the request.
    • POP Server connects directly to the public target.
    • POP Server relays target data back to Client Agent through LayLink frames.
  • KCP acceleration requirement:
    • The Agent-to-POP transport should support a KCP-over-UDP mode.
    • TCP framed mode should remain as a fallback/control-friendly transport.
    • KCP should be introduced behind a transport abstraction instead of leaking into session, policy, or routing code.
  • Transport protocol configuration:
    • POP Server uses POP_ALLOWED_AGENT_TRANSPORTS to allow one or more Agent-to-POP transports.
    • Agent uses AGENT_TRANSPORT_PROTOCOL to choose one concrete transport.
    • Allowed names are tcp, udp, and kcp.
    • Current runnable implementation is tcp; udp and kcp are reserved and must be implemented behind a transport abstraction.
  • Feasibility:
    • Workerman supports long-running async TCP servers and custom protocols; it is suitable for the framed fallback/control channel.
    • KCP itself is a UDP-based reliable ARQ protocol, so adding KCP means adding a UDP transport layer and session demultiplexing below the existing LayLink frame protocol.
    • Native PHP KCP support is possible through an extension or FFI binding to ikcp.c; pure-PHP KCP is not recommended for production performance.
    • Recommended implementation order:
      1. Complete Client Agent naming migration in code, docs, config, and entrypoints.
      2. Implement TCP-framed Client Agent -> POP -> public target path.
      3. Define TransportInterface so frame protocol can run over TCP now and KCP later.
      4. Add KCP-over-UDP transport via extension/FFI/proxy after the TCP framed path is stable.
    • Main risk:
      • KCP is not a socket by itself. It needs UDP I/O, timers, packet flush/update scheduling, MTU handling, retransmission tuning, and connection/session management.
      • PHP-only KCP may work as a prototype but is likely CPU-heavy under concurrency.
      • The cleanest production path is a PHP extension/FFI binding or a sidecar KCP transport process.

Completed in this checkpoint:

  • Added Composer PSR-4 autoload for LayLink\\.
  • Added .env.example, config/nodes.php, config/policies.php, and config/routes.php.
  • Added Workerman CLI entrypoints:
    • bin/pop-server.php
    • bin/client-agent.php
  • Added length-prefixed JSON frame protocol:
    • Frame
    • FrameType
    • FrameCodec
    • FrameParser
  • Added POP-side MVP services:
    • agent listener with node token auth, heartbeat handling, node registry, and framed session relay
  • Added Agent-side MVP client:
    • outbound POP connection
    • AUTH frame
    • heartbeat
    • local allowlist enforcement
    • target TCP connection and DATA/CLOSE relay
  • Added local JSONL audit logger at runtime/audit.log.
  • Configured Workerman logs and pid files under runtime/.
  • Added readme.md with node type descriptions, per-role .env requirements, config examples, and deployment checklist.
  • Ran composer dump-autoload.
  • Verified all non-vendor PHP files with php -l.
  • Verified PSR-4 autoload by instantiating LayLink\\Protocol\\Frame.
  • Verified POP Workerman startup with localhost high port outside the sandbox:
    • POP_AGENT_LISTEN=127.0.0.1:19001
    • worker reached [OK] and stopped cleanly via timeout.
  • Added Agent-to-POP transport configuration:
    • POP_ALLOWED_AGENT_TRANSPORTS
    • AGENT_TRANSPORT_PROTOCOL
    • POP rejects disallowed Agent transport during node authentication with transport_not_allowed.
  • Renamed the agent entrypoint and defaults:
    • Client Agent entrypoint is bin/client-agent.php
    • default NODE_ID=client-01
    • default NODE_TYPE=client
    • runtime pid file runtime/client-agent.pid
    • worker name laylink-client-agent
  • Verified bin/client-agent.php starts under Workerman and reaches [OK] in a short smoke test.
  • Reworked new MVP data path:
    • local client connects to one enabled Client Agent ingress listener
    • Client Agent sends OPEN to POP Server
    • POP Server authenticates client request and checks policy
    • POP Server opens the public target directly
    • DATA and CLOSE frames relay the stream between Client Agent and POP Server
  • Added Client Agent local ingress protocols:
    • socks5: SOCKS5 CONNECT; default enabled on 127.0.0.1:1080
    • http-proxy: HTTP CONNECT and ordinary HTTP absolute URL proxy requests; default disabled on 127.0.0.1:8080
    • raw-json: newline JSON debug ingress; default disabled on 127.0.0.1:9000
  • Added per-ingress env switches, listen IPs, and listen ports:
    • CLIENT_AGENT_SOCKS5_ENABLED
    • CLIENT_AGENT_SOCKS5_LISTEN_IP
    • CLIENT_AGENT_SOCKS5_LISTEN_PORT
    • CLIENT_AGENT_SOCKS5_AUTH_MODE
    • CLIENT_AGENT_SOCKS5_USERNAME
    • CLIENT_AGENT_SOCKS5_PASSWORD
    • CLIENT_AGENT_HTTP_PROXY_ENABLED
    • CLIENT_AGENT_HTTP_PROXY_LISTEN_IP
    • CLIENT_AGENT_HTTP_PROXY_LISTEN_PORT
    • CLIENT_AGENT_RAW_JSON_ENABLED
    • CLIENT_AGENT_RAW_JSON_LISTEN_IP
    • CLIENT_AGENT_RAW_JSON_LISTEN_PORT
  • Added Client Agent default identity for generated proxy requests:
    • CLIENT_AGENT_AUTH_TOKEN
    • CLIENT_AGENT_USER_ID
  • Completed SOCKS5 TCP proxy protocol handling for the current MVP:
    • method negotiation
    • no-auth method
    • RFC1929 username/password method
    • IPv4/domain/IPv6 target address parsing
    • CONNECT
    • standard SOCKS5 failure replies
    • BIND returns command-not-supported
    • UDP ASSOCIATE returns a local UDP relay endpoint and uses LayLink UDP_DATA frames
  • Added LayLink UDP datagram relay path:
    • Client Agent parses SOCKS5 UDP request packets
    • Client Agent sends UDP_DATA frames to POP Server
    • POP Server validates client auth and protocol=udp policy
    • POP Server sends datagrams to public UDP targets
    • POP Server returns UDP responses as UDP_DATA
  • Added UDP egress sample policy public-udp-egress for ports 53, 123, and 443.
  • Added FrameType::descriptions() as the code-level frame type catalog.
  • Verified Client Agent can start SOCKS5, HTTP proxy, and raw-json listeners together on localhost high ports.
  • Verified Client Agent can start SOCKS5 TCP plus SOCKS5 UDP relay listeners together on localhost high ports.
  • Added scripts/verify-socks5.sh to verify real SOCKS5 HTTPS requests:
    • https://bing.com/ for connectivity and HTTPS support
    • https://ip.sb/ for egress IP
  • Reorganized .env.example into readable sections:
    • [config]
    • [client-agent]
    • [pop-server]
    • Section headers are comments-for-humans in practice; the current Env loader ignores lines without =.
  • Removed deprecated/compatibility-only surfaces:
    • POP_CLIENT_LISTEN
    • POP direct client listener
    • src/Server/ClientListener.php
    • bin/client-gateway.php
    • src/Client/ClientGateway.php
    • bin/border-agent.php
    • sample border node and border policy docs
  • Verified POP now starts with only laylink-pop-agent-listener.

Known MVP limitations:

  • The current sandbox cannot bind TCP sockets; startup smoke tests need escalation or a normal shell environment.
  • raw-json debug ingress uses newline-delimited JSON before switching to raw tunnel mode. Example:
{"auth_token":"dev-token","user_id":"admin","target_host":"example.com","target_port":443,"protocol":"tcp"}
  • No TLS yet.
  • No production-grade client identity yet; dev-token is hardcoded for MVP development.
  • No automated integration test harness yet.
  • No explicit idle timeout or connect timeout enforcement yet.
  • UDP relay is datagram-oriented and currently creates short-lived POP-side UDP sockets per outbound datagram; pooling and stronger timeout accounting are still future work.
  • HTTP proxy supports CONNECT and ordinary absolute URL HTTP requests; advanced proxy auth and full HTTP/2 proxying are not implemented.

Next recommended tasks:

  1. Add a local integration harness that starts POP, Client Agent, and a mock TCP echo target, then verifies authorized tunnel, policy denial, and agent local denial.
  2. Add configurable client auth token or JWT-ready auth interface.
  3. Add target connect timeout and session idle timeout.
  4. Add buffer full/drain handling with audit result buffer_overflow.
  5. Add README quickstart with exact local commands.
  6. Optimize UDP relay with POP-side UDP socket pooling.
  7. Add UDP association idle timeouts and cleanup.
  8. Aggregate UDP audit records per association instead of per datagram.
  9. Add UDP and per-user rate limiting.

0. Project Name

LayLink

This project implements a PHP Workerman-based reverse tunnel gateway.

The system allows a Client Agent to establish an outbound persistent framed connection to a POP Server. The POP Server authenticates clients, enforces access policy, selects a route, and forwards authorized TCP streams to public Internet targets or later restricted network zones.

This is not a full Layer-3 VPN. It is a policy-controlled Layer-4 reverse access gateway.


1. Core Architecture

1.1 Node Types

The MVP contains two core logical node types:

  1. POP Server
  2. Client Agent

1.2 Required Topology

Client
  |
  v
POP Server
  |
  +--> Direct public egress
  |
  +--> Client Agent framed access

1.3 Network Constraints

The Client Agent is located on the client side.

The Client Agent:

  • Accepts local or LAN client connections.
  • Initiates outbound connections to popserver1, for example 10.1.0.2.
  • Wraps client requests and stream data in LayLink frames.

The POP Server:

  • Accepts user/client access.
  • Maintains persistent connections from Client Agents.
  • Performs authentication, authorization, route selection, session management, and auditing.
  • Can optionally connect directly to public Internet destinations.

2. Non-Negotiable Design Principles

2.1 POP Server Owns Policy

The POP Server is the only component allowed to make authorization decisions.

Agents must not accept arbitrary user-specified forwarding requests.

Agents only execute explicit OPEN instructions issued by the POP Server after authorization.

2.2 Agents Are Controlled Executors

Client Agents are controlled executors.

They may:

  • Authenticate themselves to the POP Server.
  • Maintain heartbeat.
  • Accept local client connections on explicitly configured local proxy listeners.
  • Send OPEN instructions to the POP Server.
  • Relay stream data.
  • Close sessions.

They must not:

  • Expose a public SOCKS5/HTTP proxy unless explicitly configured and protected.
  • Make authorization decisions locally.
  • Override POP policy.
  • Route traffic outside POP authorization.

2.3 No Full VPN in MVP

The MVP must not implement TUN/TAP, virtual network interfaces, routing tables, or full Layer-3 VPN behavior.

The MVP only supports authorized TCP stream forwarding.

UDP support may be added later.


3. MVP Scope

The first implementation must support:

  1. POP Server starts a TCP listener for clients.
  2. POP Server starts a TCP listener for agents.
  3. Client Agent connects outbound to POP Server.
  4. Agent authenticates with node_id and node_token.
  5. Client connects to POP Server and requests access to a target.
  6. POP Server checks policy.
  7. POP Server selects a route.
  8. POP Server sends OPEN frame to selected Agent.
  9. Agent connects to the target service.
  10. POP Server relays bidirectional TCP data between client and agent.
  11. Session closes cleanly on either side disconnecting.
  12. Audit log records the session.

MVP does not need:

  • UDP relay.
  • Web UI.
  • Multi-POP clustering.
  • Distributed HA.
  • TLS certificate automation.
  • SSH command audit.
  • Database SQL audit.
  • Complex identity provider integration.

Language:

PHP 8.2+

Framework:

Workerman

Recommended packages:

workerman/workerman
monolog/monolog
vlucas/phpdotenv
ramsey/uuid

Optional later:

firebase/php-jwt
illuminate/database
react/promise

5. Directory Structure

The project should use the following structure:

pop-tunnel-gateway/
  composer.json
  .env.example
  README.md
  CONTRACT.md

  bin/
    pop-server.php
    client-agent.php

  config/
    routes.php
    nodes.php
    policies.php

  src/
    Protocol/
      Frame.php
      FrameType.php
      FrameCodec.php
      FrameParser.php

    Server/
      PopServer.php
      AgentListener.php

    Agent/
      AgentClient.php
      TargetConnector.php

    Client/
      ClientGateway.php

    Session/
      TunnelSession.php
      SessionManager.php

    Node/
      NodeRegistry.php
      NodeConnection.php

    Auth/
      NodeAuthenticator.php
      ClientAuthenticator.php
      PolicyChecker.php

    Route/
      RouteResolver.php
      RouteDecision.php

    Audit/
      AuditLogger.php

    Util/
      BufferLimiter.php
      LoggerFactory.php

6. Frame Protocol

The system must use a framed protocol between POP Server and Agents.

Raw stream passthrough between POP and Agent is not allowed because the system needs multiplexing, session IDs, heartbeats, error handling, and auditability.

6.1 Frame Types

Required frame types:

Type Direction Meaning
AUTH Client Agent -> POP Agent authenticates itself with node_id, node_type, node_token, and transport_protocol.
AUTH_OK POP -> Client Agent Agent authentication accepted.
AUTH_FAIL POP -> Client Agent Agent authentication rejected; POP closes the connection after sending this frame.
PING Client Agent -> POP Agent heartbeat with active session count, load, and timestamp.
PONG POP -> Client Agent Heartbeat response.
OPEN Client Agent -> POP Client Agent requests POP to authorize and open a target stream.
OPEN_OK POP -> Client Agent POP has connected the target and the stream can begin.
OPEN_FAIL POP -> Client Agent POP rejected or failed the requested target stream.
DATA Bidirectional Stream bytes for one session_id; MVP payload uses base64.
UDP_DATA Bidirectional UDP datagram bytes for one UDP association; MVP payload uses base64 and includes target metadata.
CLOSE Bidirectional Close one stream session.
ERROR Bidirectional Explicit protocol or session error.
WINDOW Bidirectional Reserved flow-control window update for future backpressure.

For the new MVP, OPEN always means:

Client Agent asks POP Server to connect to the target.

It does not mean POP asks Agent to connect to an intranet target. That older direction is reserved for a later executor-agent mode.

6.2 Frame Fields

Each frame must contain:

version
type
session_id
payload_length
payload

Suggested JSON payload for MVP is acceptable.

Binary optimization may be added later.

6.3 Frame Encoding

For MVP, use length-prefixed JSON frames.

Format:

uint32_be length
json_payload

Example decoded frame:

{
  "version": 1,
  "type": "DATA",
  "session_id": "018f6f4a-xxxx-xxxx",
  "payload": "base64-encoded-binary"
}

For DATA frames, binary stream data may be base64 encoded in MVP.

A later version may replace this with binary headers plus raw binary body.


7. Agent Authentication

When an Agent connects to POP Server, it must immediately send an AUTH frame.

Example:

{
  "version": 1,
  "type": "AUTH",
  "session_id": null,
  "payload": {
    "node_id": "client-01",
    "node_type": "client",
    "node_zone": "corp",
    "node_token": "CHANGE_ME",
    "supported_protocols": ["tcp"]
  }
}

POP Server must verify:

node_id exists
node_token matches
node_type matches config
node is not disabled

On success:

{
  "version": 1,
  "type": "AUTH_OK",
  "session_id": null,
  "payload": {
    "node_id": "client-01",
    "heartbeat_interval": 10
  }
}

On failure:

{
  "version": 1,
  "type": "AUTH_FAIL",
  "session_id": null,
  "payload": {
    "reason": "invalid_node_token"
  }
}

POP Server must close the connection after AUTH_FAIL.


8. Heartbeat

Agents must send PING every 10 seconds by default.

Example:

{
  "version": 1,
  "type": "PING",
  "session_id": null,
  "payload": {
    "node_id": "client-01",
    "active_sessions": 12,
    "load": 0.35,
    "timestamp": 1710000000
  }
}

POP Server replies:

{
  "version": 1,
  "type": "PONG",
  "session_id": null,
  "payload": {
    "timestamp": 1710000001
  }
}

If no heartbeat is received for 30 seconds, POP Server must mark the node as offline and close all sessions routed through that node.


9. Client Request Model

For MVP, the client may connect directly to a POP TCP listener and submit an initial JSON request.

Example:

{
  "auth_token": "dev-token",
  "target_host": "192.168.10.20",
  "target_port": 22,
  "protocol": "tcp",
  "route_hint": "client-01"
}

After the initial request is accepted, the TCP stream becomes a bidirectional tunnel.

Later versions may implement:

SOCKS5
HTTP CONNECT
WebSocket tunnel
mTLS client identity
JWT authentication

10. Route Selection

The POP Server must use RouteResolver to decide where traffic should go.

Possible route types:

direct
agent
border
reject

Example route decision:

[
    'allowed' => true,
    'route_type' => 'agent',
    'node_id' => 'client-01',
    'policy_id' => 'corp-ssh-admin',
]

Client route_hint is advisory only.

The POP Server may ignore, override, or reject the route hint.


11. Policy Rules

Policies must be defined in config/policies.php.

Example:

return [
    [
        'policy_id' => 'corp-ssh-admin',
        'users' => ['admin', 'devops'],
        'target_hosts' => ['192.168.10.20', '192.168.10.21'],
        'target_ports' => [22],
        'route_type' => 'agent',
        'node_id' => 'client-01',
        'enabled' => true,
    ],
    [
        'policy_id' => 'public-web-egress',
        'users' => ['normal-user', 'admin'],
        'target_hosts' => ['*'],
        'target_ports' => [80, 443],
        'route_type' => 'direct',
        'enabled' => true,
    ],
];

Policy matching must consider:

user identity
target host
target port
protocol
requested route
node availability
policy enabled/disabled state

Default behavior must be deny.


12. Agent Local Allowlist

Each Agent must enforce its own local allowlist.

Example config/nodes.php:

return [
    'client-01' => [
        'node_type' => 'client',
        'token' => 'CHANGE_ME',
        'allowed_cidrs' => [
            '192.168.0.0/16',
            '10.10.0.0/16',
        ],
        'allowed_ports' => [22, 80, 443, 3306, 5432],
        'enabled' => true,
    ],
];

If an Agent receives an OPEN request outside its local allowlist, it must return OPEN_FAIL.

Example:

{
  "version": 1,
  "type": "OPEN_FAIL",
  "session_id": "018f6f4a-xxxx",
  "payload": {
    "reason": "agent_local_policy_denied"
  }
}

13. Opening a Target Connection

POP Server sends:

{
  "version": 1,
  "type": "OPEN",
  "session_id": "018f6f4a-xxxx",
  "payload": {
    "target_host": "192.168.10.20",
    "target_port": 22,
    "protocol": "tcp",
    "user_id": "admin",
    "policy_id": "corp-ssh-admin"
  }
}

Agent connects to the target.

On success:

{
  "version": 1,
  "type": "OPEN_OK",
  "session_id": "018f6f4a-xxxx",
  "payload": {
    "target_host": "192.168.10.20",
    "target_port": 22
  }
}

On failure:

{
  "version": 1,
  "type": "OPEN_FAIL",
  "session_id": "018f6f4a-xxxx",
  "payload": {
    "reason": "connection_refused"
  }
}

14. Data Forwarding

After OPEN_OK, data is exchanged with DATA frames.

Example:

{
  "version": 1,
  "type": "DATA",
  "session_id": "018f6f4a-xxxx",
  "payload": {
    "data": "base64-encoded-binary"
  }
}

Both POP Server and Agent must map session_id to the corresponding local TCP connection.


15. Session Lifecycle

Session states:

NEW
OPENING
OPEN
CLOSING
CLOSED
FAILED

State transitions:

NEW -> OPENING
OPENING -> OPEN
OPENING -> FAILED
OPEN -> CLOSING
CLOSING -> CLOSED
OPEN -> CLOSED

A session must be closed when:

client disconnects
target disconnects
agent disconnects
policy check fails
OPEN_FAIL is received
send buffer exceeds hard limit
idle timeout is reached

16. Timeouts

Required timeout defaults:

agent heartbeat interval: 10 seconds
agent offline threshold: 30 seconds
target connect timeout: 5 seconds
session idle timeout: 300 seconds
client initial request timeout: 5 seconds

All timeout values should be configurable.


17. Buffer and Backpressure

The implementation must avoid unbounded memory growth.

Each Workerman connection should configure a maximum send buffer.

Suggested default:

$connection->maxSendBufferSize = 8 * 1024 * 1024;

The implementation must handle:

onBufferFull
onBufferDrain
onClose
onError

If a session exceeds buffer limits and cannot recover, close the session and write an audit log entry.


18. Audit Logging

Every session must produce an audit log.

Required fields:

session_id
user_id
source_ip
target_host
target_port
protocol
route_type
node_id
policy_id
start_time
end_time
duration_ms
bytes_client_to_target
bytes_target_to_client
result
failure_reason

MVP may write JSON lines to a local file:

runtime/audit.log

Example:

{
  "session_id": "018f6f4a-xxxx",
  "user_id": "admin",
  "source_ip": "1.2.3.4",
  "target_host": "192.168.10.20",
  "target_port": 22,
  "protocol": "tcp",
  "route_type": "agent",
  "node_id": "client-01",
  "policy_id": "corp-ssh-admin",
  "start_time": "2026-05-28T10:00:00+08:00",
  "end_time": "2026-05-28T10:01:00+08:00",
  "duration_ms": 60000,
  "bytes_client_to_target": 1024,
  "bytes_target_to_client": 2048,
  "result": "closed",
  "failure_reason": null
}

19. Error Handling

Errors must be explicit.

Required error reasons include:

invalid_frame
invalid_auth
node_not_found
node_offline
policy_denied
route_not_found
target_connect_timeout
target_connection_refused
agent_local_policy_denied
session_not_found
buffer_overflow
protocol_not_supported
internal_error

Do not silently drop sessions without logging.


20. Security Requirements

20.1 Required for MVP

  • Node token authentication.
  • Default-deny policy.
  • Agent local allowlist.
  • Audit logging.
  • Explicit route decision.
  • No arbitrary target access.
  • No unauthenticated Agent registration.
  • No unauthenticated client request in production mode.

20.2 Required Before Production

  • TLS between Client and POP.
  • TLS between Agent and POP.
  • Strong node credentials.
  • Rotatable node tokens.
  • JWT or mTLS client authentication.
  • Per-user policy.
  • Rate limiting.
  • Session concurrency limits.
  • Structured audit storage.
  • Log redaction for secrets.

21. Configuration

.env.example:

APP_ENV=dev
POP_AGENT_LISTEN=0.0.0.0:9001
NODE_ID=client-01
NODE_TYPE=client
NODE_TOKEN=CHANGE_ME
POP_SERVER_ADDRESS=tcp://10.1.0.2:9001
AUDIT_LOG=runtime/audit.log
LOG_LEVEL=debug

22. CLI Entrypoints

22.1 Start POP Server

php bin/pop-server.php start

22.2 Start Client Agent

php bin/client-agent.php start

22.3 Stop Services

php bin/pop-server.php stop
php bin/client-agent.php stop

23. MVP Acceptance Tests

The implementation is acceptable only if the following tests pass.

23.1 Agent Registration

Given a valid node token, Client Agent connects to POP Server and becomes online.

Expected result:

NodeRegistry contains client-01 as online.

23.2 Invalid Agent Rejected

Given an invalid node token, POP Server returns AUTH_FAIL and closes the connection.

Expected result:

Node is not registered.
Audit/security log records invalid_auth.

23.3 Authorized TCP Tunnel

Given:

Client requests 192.168.10.20:22
User is allowed by policy
client-01 is online

Expected result:

POP sends OPEN to client-01.
Agent connects to 192.168.10.20:22.
Client can exchange TCP data with target.
Audit log records success.

23.4 Policy Denial

Given:

Client requests 192.168.99.99:22
No policy allows this target

Expected result:

POP rejects the request.
No OPEN frame is sent to Agent.
Audit log records policy_denied.

23.5 Agent Local Denial

Given:

POP sends OPEN to a target outside Agent local allowlist

Expected result:

Agent returns OPEN_FAIL with agent_local_policy_denied.
Session is closed.
Audit log records failure.

23.6 Agent Offline

Given:

client-01 is offline
Client requests route through client-01

Expected result:

POP rejects request with node_offline.

23.7 Clean Close

Given:

Client closes connection

Expected result:

POP sends CLOSE to Agent.
Agent closes target connection.
SessionManager removes session.
Audit log records closed.

23.8 Target Connection Failure

Given:

Target host or port is unreachable

Expected result:

Agent sends OPEN_FAIL.
POP closes client connection.
Audit log records target connection failure.

24. Implementation Priority

Implement in this order:

  1. Frame, FrameCodec, FrameParser
  2. NodeAuthenticator
  3. NodeRegistry
  4. Agent connection listener on POP Server
  5. Client Agent outbound connection
  6. Heartbeat
  7. Client listener
  8. Policy checker
  9. Route resolver
  10. Session manager
  11. Agent target connector
  12. DATA forwarding
  13. CLOSE handling
  14. Audit logger
  15. Basic tests

Do not implement UDP transport, KCP transport, Web UI, or clustering before the MVP TCP-framed proxy path is stable.


25. Coding Rules

  • Use strict types where possible.
  • Keep protocol parsing separate from business logic.
  • Do not mix policy checking with socket forwarding.
  • Do not use global mutable arrays except Workerman bootstrap state if unavoidable.
  • All session IDs must be unique.
  • Every network error must be logged.
  • Every rejected access must be auditable.
  • All config values must be externalized.
  • The system must run on Linux.
  • The code must be readable and modular, not a single giant script.

26. Deliverables

Codex should generate:

composer.json
.env.example
README.md
bin/pop-server.php
bin/client-agent.php
config/routes.php
config/nodes.php
config/policies.php
src/**/*.php

The generated code must be runnable with:

composer install
php bin/pop-server.php start
php bin/client-agent.php start

27. Out of Scope for First Version

The following features must not be implemented in the first version unless explicitly requested later:

TUN/TAP VPN
Layer-3 routing
Kernel packet capture
UDP relay
QUIC
Web dashboard
Clustered POP Server
Redis-based session sharing
TLS certificate automation
SSH command recording
Database SQL auditing
Browser-based remote desktop

28. Final Goal

The final MVP should prove this flow:

Client
  -> POP Server
  -> policy check
  -> Client Agent
  -> internal TCP service

The POP Server must remain the only policy authority.

Agents must remain controlled executors.

Default access must always be denied unless a policy explicitly allows it.