GitHub

What is Certstream?

Real-time certificate transparency log aggregation

Overview

Certstream aggregates certificates from Certificate Transparency logs and streams them in real-time. This Rust implementation is a drop-in replacement for certstream-server (Elixir) and certstream-server-go with better performance.

Why Rust?

  • ~12MB memory idle, ~19MB under load
  • Sub-millisecond latency (tested 0.33ms min)
  • Handles 50,000+ concurrent connections
  • ~6% CPU with 500+ clients
  • Single binary, no dependencies

Endpoints

Connect using WebSocket or Server-Sent Events

Endpoint Protocol Description
wss://ws.certstream.dev/ WebSocket Lite stream (no DER/chain data)
wss://ws.certstream.dev/full-stream WebSocket Full stream with DER and certificate chain
wss://ws.certstream.dev/domains-only WebSocket Domain names only (minimal data)
https://ws.certstream.dev/sse SSE Server-Sent Events lite stream
https://ws.certstream.dev/sse?stream=full SSE Server-Sent Events full stream
https://ws.certstream.dev/sse?stream=domains SSE Server-Sent Events domains only
https://ws.certstream.dev/health HTTP Basic health check (returns "OK")
https://ws.certstream.dev/health/deep HTTP Detailed health with log status, connections, uptime (JSON)
https://ws.certstream.dev/metrics HTTP Prometheus metrics
https://ws.certstream.dev/example.json HTTP Sample message format

Message Format

JSON structure for certificate update messages

JSON certificate_update
{
  "message_type": "certificate_update",
  "data": {
    "update_type": "X509LogEntry",
    "leaf_cert": {
      "subject": {
        "CN": "example.com",
        "O": "Example Inc",
        "C": "US",
        "aggregated": "/C=US/CN=example.com/O=Example Inc"
      },
      "issuer": {
        "CN": "R3",
        "O": "Let's Encrypt",
        "aggregated": "/CN=R3/O=Let's Encrypt"
      },
      "serial_number": "048A3F...",
      "not_before": 1703721600,
      "not_after": 1735257600,
      "fingerprint": "AB:CD:EF:01:...",
      "sha1": "AB:CD:EF:01:...",
      "sha256": "AB:CD:EF:01:...",
      "signature_algorithm": "sha256, rsa",
      "is_ca": false,
      "all_domains": [
        "example.com",
        "www.example.com"
      ],
      "extensions": {
        "keyUsage": "Digital Signature, Key Encipherment",
        "extendedKeyUsage": "serverAuth, clientAuth",
        "basicConstraints": "CA:FALSE",
        "subjectAltName": "DNS:example.com, DNS:www.example.com"
      },
      "as_der": "BASE64..."  // full-stream only
    },
    "chain": [...],  // full-stream only
    "cert_index": 1234567890,
    "seen": 1703808000.123,
    "source": {
      "url": "ct.googleapis.com/logs/argon2025",
      "name": "Google Argon 2025"
    }
  }
}

Stream Types

Choose the right stream for your use case

Lite Stream (default)

Contains certificate metadata, domains, issuer info, and timestamps. No DER-encoded certificate data or chain. Best for most monitoring use cases.

Full Stream

Includes everything in lite stream plus base64-encoded DER certificate and full chain. Use when you need to verify or store complete certificates.

Domains Only

Minimal payload with just domain names, timestamp, and source. Lowest bandwidth option for domain-focused monitoring like phishing detection.

Quick Start

Connect and start receiving certificates in seconds

JavaScript Browser
const ws = new WebSocket('wss://ws.certstream.dev/');

ws.onmessage = (event) => {
    const data = JSON.parse(event.data);
    if (data.message_type === 'certificate_update') {
        console.log(data.data.leaf_cert.all_domains);
    }
};
cURL SSE
# Lite stream
curl -N https://ws.certstream.dev/sse

# Full stream
curl -N "https://ws.certstream.dev/sse?stream=full"

# Domains only
curl -N "https://ws.certstream.dev/sse?stream=domains"

Client Examples

Connect using your preferred language

Python certstream
# pip install certstream
import certstream

def callback(message, context):
    if message['message_type'] == 'certificate_update':
        domains = message['data']['leaf_cert']['all_domains']
        print(domains)

certstream.listen_for_events(callback, url='ws://localhost:8080/')
Go certstream-go
import "github.com/CaliDog/certstream-go"

stream, _ := certstream.CertStreamEventStream(false)
for event := range stream {
    fmt.Println(event.Data.LeafCert.AllDomains)
}
JavaScript Node.js
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:8080/');

ws.on('message', (data) => {
    const msg = JSON.parse(data);
    if (msg.message_type === 'certificate_update') {
        console.log(msg.data.leaf_cert.all_domains);
    }
});
Bash With Auth
# WebSocket with token
wscat -c ws://localhost:8080/ -H "Authorization: Bearer your-token"

# SSE with token
curl -H "Authorization: Bearer your-token" http://localhost:8080/sse

Self-Hosting

Run your own instance with Docker or from source

Docker Recommended
# Quick start
docker run -d -p 8080:8080 -p 8081:8081 reloading01/certstream-server-rust:latest

# With custom config
docker run -d \
  -p 8080:8080 \
  -p 8081:8081 \
  -v ./config.yaml:/app/config.yaml \
  -v ./state:/app/state \
  reloading01/certstream-server-rust:latest

# Docker Compose
version: '3.8'
services:
  certstream:
    image: reloading01/certstream-server-rust:latest
    ports:
      - "8080:8080"
      - "8081:8081"
    volumes:
      - ./config.yaml:/app/config.yaml
      - ./state:/app/state
    restart: unless-stopped
Build From Source
# Docker build
docker build -t certstream-server-rust .
docker run -d -p 8080:8080 certstream-server-rust

# Cargo build
cargo build --release
./target/release/certstream-server-rust

# Docker Compose
docker compose up -d

Environment Variables

Configure the server using environment variables

General Settings

Variable Default Description
CERTSTREAM_HOST 0.0.0.0 Bind address
CERTSTREAM_PORT 8080 HTTP/WebSocket port
CERTSTREAM_LOG_LEVEL info debug, info, warn, error
CERTSTREAM_BUFFER_SIZE 1000 Broadcast buffer

Protocols

Variable Default Description
CERTSTREAM_WS_ENABLED true Enable WebSocket
CERTSTREAM_SSE_ENABLED false Enable SSE
CERTSTREAM_METRICS_ENABLED true Enable /metrics endpoint
CERTSTREAM_HEALTH_ENABLED true Enable /health endpoint
CERTSTREAM_EXAMPLE_JSON_ENABLED true Enable /example.json endpoint

Connection Limiting

Variable Default Description
CERTSTREAM_CONNECTION_LIMIT_ENABLED false Enable connection limits
CERTSTREAM_CONNECTION_LIMIT_MAX_CONNECTIONS 10000 Max total connections
CERTSTREAM_CONNECTION_LIMIT_PER_IP_LIMIT 100 Max per IP

Authentication

Variable Default Description
CERTSTREAM_AUTH_ENABLED false Enable token auth
CERTSTREAM_AUTH_TOKENS - Comma-separated tokens
CERTSTREAM_AUTH_HEADER_NAME Authorization Auth header

CT Log Settings

Variable Default Description
CERTSTREAM_CT_LOG_STATE_FILE certstream_state.json State file path
CERTSTREAM_CT_LOG_RETRY_MAX_ATTEMPTS 3 Max retry attempts
CERTSTREAM_CT_LOG_REQUEST_TIMEOUT_SECS 30 Request timeout
CERTSTREAM_CT_LOG_BATCH_SIZE 256 Entries per batch

Hot Reload

Variable Default Description
CERTSTREAM_HOT_RELOAD_ENABLED false Enable hot reload
CERTSTREAM_HOT_RELOAD_WATCH_PATH - Config file to watch

Configuration File

Full YAML configuration example

YAML config.yaml
host: "0.0.0.0"
port: 8080
log_level: "info"
buffer_size: 1000
ct_logs_url: "https://www.gstatic.com/ct/log_list/v3/all_logs_list.json"

protocols:
  websocket: true
  sse: true
  metrics: true
  health: true
  example_json: true

ct_log:
  state_file: "/data/state.json"
  batch_size: 256
  poll_interval_ms: 500
  retry_max_attempts: 3
  request_timeout_secs: 30

connection_limit:
  enabled: true
  max_connections: 10000
  per_ip_limit: 100

auth:
  enabled: false
  tokens:
    - "secret-token-1"
    - "secret-token-2"
  header_name: "Authorization"

hot_reload:
  enabled: true

custom_logs:
  - name: "My Custom CT Log"
    url: "https://ct.example.com/log"

# Static CT logs (Sunlight / static-ct-api protocol)
# Use monitoring prefix (mon.*) for read access
static_logs:
  - name: "Let's Encrypt 'Willow' 2025h2d"
    url: "https://mon.willow.ct.letsencrypt.org/2025h2d/"
  - name: "Let's Encrypt 'Willow' 2026h1"
    url: "https://mon.willow.ct.letsencrypt.org/2026h1/"
  - name: "Let's Encrypt 'Sycamore' 2025h2d"
    url: "https://mon.sycamore.ct.letsencrypt.org/2025h2d/"
  - name: "Let's Encrypt 'Sycamore' 2026h1"
    url: "https://mon.sycamore.ct.letsencrypt.org/2026h1/"

Config search order: CERTSTREAM_CONFIG env var → ./config.yaml./config.yml/etc/certstream/config.yaml

Performance

Load tested with 500 concurrent WebSocket clients

Metric Rust Go
Memory (idle) ~26 MB ~100 MB
Memory (avg under load) 22 MB 254 MB
CPU (avg under load) ~29% ~76%
Latency (avg) 3.4ms 31ms
Latency (min) 0.16ms 1.7ms
Throughput 677K msg 267K msg

Result: 12x less memory, 9x faster latency, 2.5x higher throughput.

Prometheus Metrics

Key metrics available at the /metrics endpoint

Core Metrics

Metric Type Description
certstream_messages_sent counter Total certificate messages broadcast
certstream_parse_failures counter Failed certificate parses (RFC 6962)
certstream_ct_logs_count gauge Number of RFC 6962 CT logs monitored
certstream_worker_panics counter Worker panic count (auto-recovered)

Static CT Metrics (v1.2.0)

Metric Type Description
certstream_static_ct_logs_count gauge Number of static CT logs monitored
certstream_static_ct_tiles_fetched counter Total tiles fetched from static CT logs
certstream_static_ct_entries_parsed counter Entries successfully parsed from tiles
certstream_static_ct_checkpoint_errors counter Checkpoint fetch/parse failures
certstream_issuer_cache_size gauge Cached issuer certificates
certstream_duplicates_filtered counter Duplicate certificates filtered by dedup
certstream_dedup_cache_size gauge Current dedup cache entries

CT Logs Monitored

49+ RFC 6962 logs + static CT logs with cross-log deduplication

Google

Argon, Xenon, Solera, Submariner

Cloudflare

Nimbus

DigiCert

Wyvern, Sphinx

Sectigo

Elephant, Tiger, Dodo

Let's Encrypt (Static CT)

Willow 2025h2/2026h1, Sycamore 2025h2/2026h1

Others

TrustAsia, Nordu, and more