Overview
EyeLog uses a dual-channel architecture that separates control signals from data streaming. This separation provides better reliability, performance, and security.
┌─────────────────────────────────────────────────────────────────────────┐
│ AGENT │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │
│ │ CONTROL FEATURES │ │ INFO FEATURES │ │
│ │ (gRPC) │ │ (QUIC) │ │
│ │ │ │ │ │
│ │ • Receive commands │ │ • Send logs │ │
│ │ • Execute actions │ │ • Send metrics │ │
│ │ • Apply configuration │ │ • Send inventory │ │
│ │ • Acknowledge results │ │ • Send security events │ │
│ │ │ │ │ │
│ │ Collector TELLS agent │ │ Agent TELLS collector │ │
│ │ what to DO │ │ what it SEES │ │
│ │ │ │ │ │
│ └──────────────┬──────────────┘ └──────────────┬──────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ │
│ │ gRPC │ │ QUIC │ │
│ │ :9443 │ │ :5514 │ │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
└─────────────────┼───────────────────────────────────┼────────────────────┘
│ │
└───────────────┬───────────────────┘
│
▼
┌────────────┐
│ COLLECTOR │
└────────────┘
Connection Model: Agent-Initiated
A fundamental design principle: the agent initiates all connections to the collector. The collector never connects inbound to agents. This dramatically simplifies enterprise deployment.
┌─────────────────────────────────────────────────────────────────────────────┐ │ AGENT-INITIATED CONNECTION MODEL │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ AGENT │ │ COLLECTOR │ │ │ │ │ │ │ │ │ │ Corporate LAN │ OUTBOUND ONLY │ Data Center / │ │ │ │ Behind NAT │ ─────────────────────────►│ Cloud │ │ │ │ Behind FW │ gRPC + QUIC │ │ │ │ │ │ │ Listening on │ │ │ │ No inbound │ Return traffic on │ :9443, :5514 │ │ │ │ ports needed │ ◄─────────────────────────│ │ │ │ │ │ established conn │ │ │ │ └─────────────────┘ └─────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
| Aspect | Traditional (Collector → Agent) | EyeLog (Agent → Collector) |
|---|---|---|
| Firewall config | Open inbound port on every endpoint | No changes needed |
| NAT traversal | Requires port forwarding | Works automatically |
| Deployment | Coordinate with network team | Just install and run |
| Security | Inbound ports = attack surface | No listening ports on endpoints |
Why this matters: In enterprise environments, opening inbound firewall rules requires change requests, security reviews, and coordination across teams. With agent-initiated connections, you deploy the agent and it just works—outbound connections are typically allowed by default, and stateful firewalls automatically permit return traffic.
Channel Comparison
| Aspect | Control Plane (gRPC) | Data Plane (QUIC) |
|---|---|---|
| Port | :9443 |
:5514 |
| Direction | Bidirectional | Agent → Collector |
| Protocol | gRPC over HTTP/2 | QUIC (UDP-based) |
| Purpose | Commands, config, heartbeat | Logs, metrics, inventory |
| Reliability | Guaranteed delivery | Best-effort with retries |
| Volume | Low (commands) | High (telemetry) |
Why gRPC for Control?
- Reliable delivery - Commands must be delivered exactly once
- Bidirectional streaming - Agent can send responses, collector can send commands
- Strong typing - Protobuf ensures type safety
- Built-in retry - Handles transient failures
- Deadline propagation - Commands have timeouts
gRPC Services
service ControlService {
// Enrollment
rpc Enroll(EnrollRequest) returns (EnrollResponse);
rpc RenewCertificate(RenewRequest) returns (RenewResponse);
// Bidirectional command stream
rpc CommandStream(stream CommandResponse) returns (stream Command);
}
Why QUIC for Data?
- High throughput - UDP-based, no head-of-line blocking
- Multiplexed streams - Multiple data types over one connection
- 0-RTT reconnection - Fast reconnect after network changes
- Built-in encryption - TLS 1.3 integrated
- Connection migration - Survives IP changes
QUIC Data Format
message InfoBatch {
string agent_id = 1;
string agent_version = 2;
uint64 batch_id = 3;
// One or more of these per batch
repeated MetricSample metrics = 10;
repeated LogEntry logs = 11;
repeated NetworkConnection connections = 12;
Inventory inventory = 13;
}
Security: Both Use mTLS
Both channels use mutual TLS authentication:
- Agent authenticates collector - Verifies server certificate
- Collector authenticates agent - Verifies client certificate
- Certificates bound to fingerprint - Cert won't work on different machine
- 7-day certificate validity - Auto-renewed before expiry
Component Diagram
┌─────────────────────────────────────────────────────────────────────────────┐ │ COLLECTOR │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ gRPC │ │ QUIC │ │ Storage │ │ Web │ │ │ │ Server │ │ Server │ │ Layer │ │ Portal │ │ │ │ :9443 │ │ :5514 │ │ │ │ :8443 │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ │ │ └──────────────────┴──────────────────┴──────────────────┘ │ │ │ │ │ ┌──────┴──────┐ │ │ │ Internal │ │ │ │ CA │ │ │ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘
Protocol Versioning
Both protocols are versioned independently to allow backward-compatible evolution:
Proto Package Naming:
├── eyelog.agent.control.v1 (gRPC Control Plane)
├── eyelog.agent.control.v2 (future)
├── eyelog.agent.info.v1 (QUIC Data Plane)
└── eyelog.agent.info.v2 (future)
Version negotiation happens on connection:
- Agent connects with its supported protocol versions
- Collector responds with the version to use
- Both sides use negotiated version
- Collector maintains handlers for all supported versions