Docs / System Architecture

System Architecture

Dual-channel design with gRPC for control and QUIC for data. Each protocol optimized for its purpose.

Version 1.0
~10 min read

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:

  1. Agent connects with its supported protocol versions
  2. Collector responds with the version to use
  3. Both sides use negotiated version
  4. Collector maintains handlers for all supported versions