Certstream API Documentation
Connect to real-time certificate streams via WebSocket or SSE
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
{
"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
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); } };
# 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
# 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/')
import "github.com/CaliDog/certstream-go" stream, _ := certstream.CertStreamEventStream(false) for event := range stream { fmt.Println(event.Data.LeafCert.AllDomains) }
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); } });
# 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
# 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
# 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
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
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