Connect to the certificate stream.
Everything you need to consume certstream over WebSocket or SSE, and to configure your own server.
What is Certstream?
Real-time Certificate Transparency log aggregation.
The idea
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.
How it stays lean
- Pre-serialized broadcast via
Arc<PreSerializedMessage>with zero-copyUtf8Bytestext frames — no per-subscriber re-encoding - Idle-server guard — JSON is skipped entirely when no clients are connected
- A single shared issuer cache across all static-CT watchers
- SIMD-accelerated JSON via
simd-json, enabled by default - Single binary, no runtime dependencies
certificate_update
JSON structure for certificate update messages.
Two timestamps appear in each message: seen is the wall-clock time this server
processed the entry, while submission_timestamp is the moment the CT log itself
accepted the certificate and issued a Signed Certificate Timestamp (SCT) — as defined by
RFC 6962 §3.1.
Both are Unix timestamps (seconds since epoch, with millisecond precision).
{
"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,
"submission_timestamp": 1703721600.456, // when the CT log accepted this cert (RFC 6962 §3.1)
"source": {
"url": "ct.googleapis.com/logs/argon2025",
"name": "Google Argon 2025"
}
}
}
dns_entries
The /domains-only WebSocket and /sse?stream=domains endpoints use a distinct schema.
{
"message_type": "dns_entries",
"data": [
"example.com",
"www.example.com",
"*.example.com"
]
}
Unlike the full and lite streams, message_type is "dns_entries" (not "certificate_update"),
and data is a bare JSON array of strings — not an object with nested fields.
Use this stream when you only need domain names and want minimal bandwidth.
Choose the right stream
Three output formats, one for each level of detail.
Lite — default
Certificate metadata, domains, issuer info, and timestamps. No DER-encoded certificate or chain. Best for most monitoring use cases.
Full
Everything in the lite stream plus the base64-encoded DER certificate and full chain. Use when you need to verify or store complete certificates.
Domains only
Just the domain names — the lowest-bandwidth option. Note the schema differs: message_type is "dns_entries" and data is a bare array of strings.
WebSocket, SSE & HTTP
Connect over your transport of choice.
WebSocket
| Endpoint | Description |
|---|---|
ws://host:8080/ | Lite stream (no DER / chain) |
ws://host:8080/full-stream | Full data with DER and chain |
ws://host:8080/domains-only | Domain names only (message_type: "dns_entries") |
SSE
| Endpoint | Description |
|---|---|
http://host:8080/sse | Lite (default) |
http://host:8080/sse?stream=full | Full |
http://host:8080/sse?stream=domains | Domains only |
HTTP
| Endpoint | Description |
|---|---|
/health | Basic health check (returns "OK") |
/health/deep | Detailed health: log status, connections, uptime (JSON) |
/metrics | Prometheus metrics |
/example.json | Example message |
REST API — opt-in
Enable with CERTSTREAM_API_ENABLED=true.
| Endpoint | Description |
|---|---|
GET /api/stats | Server statistics (uptime, connections, throughput, cache) |
GET /api/logs | CT log health status (healthy / degraded / unhealthy) |
GET /api/cert/{hash} | Lookup certificate by SHA256, SHA1, or fingerprint |
Connect from any language
Pick your stack and start consuming the stream.
# 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
Run your own instance
Docker, Docker Compose, or a build from source.
# Quick start docker run -d -p 8080:8080 \ ghcr.io/reloading01/certstream-server-rust:latest # With custom config docker run -d -p 8080:8080 \ -v ./config.yaml:/app/config.yaml \ -v ./state:/app/state \ ghcr.io/reloading01/certstream-server-rust:latest
# Cargo build cargo build --release ./target/release/certstream-server-rust # Docker Compose docker compose up -d
services: certstream: image: ghcr.io/reloading01/certstream-server-rust:latest ports: - "8080:8080" volumes: - ./config.yaml:/app/config.yaml - ./state:/app/state restart: unless-stopped
Environment variables
Every setting can be driven by an environment variable.
General
| 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 | true | 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 |
CERTSTREAM_API_ENABLED | false | Enable REST API endpoints |
Stream types
| Variable | Default | Description |
|---|---|---|
CERTSTREAM_STREAM_FULL_ENABLED | true | Full stream (DER + chain) |
CERTSTREAM_STREAM_LITE_ENABLED | true | Lite stream |
CERTSTREAM_STREAM_DOMAINS_ONLY_ENABLED | true | Domains-only stream |
Disabling a stream type removes its route and skips JSON serialization entirely, saving CPU and outbound bandwidth.
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 |
Rate limiting
| Variable | Default | Description |
|---|---|---|
CERTSTREAM_RATE_LIMIT_ENABLED | false | Enable per-IP rate limiting (token bucket + sliding window) |
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 |
CERTSTREAM_DEDUP_CAPACITY | 200000 | Cross-log dedup capacity |
CERTSTREAM_DEDUP_TTL_SECS | 900 | Dedup window (seconds) |
Hot reload
| Variable | Default | Description |
|---|---|---|
CERTSTREAM_HOT_RELOAD_ENABLED | false | Enable hot reload |
CERTSTREAM_HOT_RELOAD_WATCH_PATH | — | Config file to watch |
YAML config file
A full example covering every section.
host: "0.0.0.0" port: 8080 log_level: "info" buffer_size: 1000 protocols: websocket: true sse: true metrics: true health: true example_json: true api: false streams: full: true lite: true domains_only: true ct_log: state_file: "/data/state.json" batch_size: 256 poll_interval_ms: 500 retry_max_attempts: 3 request_timeout_secs: 30 dedup: capacity: 200000 ttl_secs: 900 connection_limit: enabled: true max_connections: 10000 per_ip_limit: 100 auth: enabled: false tokens: - "secret-token-1" header_name: "Authorization" rate_limit: enabled: false max_tokens: 100 refill_rate: 10 hot_reload: enabled: true # Static CT logs (static-ct-api protocol). # Use the monitoring prefix (mon.*) for read access. # Trusted logs are also auto-discovered from the Google + Apple log lists. static_logs: - name: "Let's Encrypt 'Willow' 2026h1" url: "https://mon.willow.ct.letsencrypt.org/2026h1/" - 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
What to expect under load
Default config, 100 concurrent WebSocket clients on the lite stream, 10-minute plateau window.
Memory plateaus within roughly five minutes of startup and holds flat — no growth over time. Reproduce
these numbers locally with the scripts in soak/.
Prometheus metrics
Key series exposed at the /metrics endpoint.
Core
| 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 | RFC 6962 CT logs monitored |
certstream_worker_panics | counter | Worker panic count (auto-recovered) |
Static CT
| Metric | Type | Description |
|---|---|---|
certstream_static_ct_logs_count | gauge | Static CT logs monitored |
certstream_static_ct_tiles_fetched | counter | Tiles fetched from static CT logs |
certstream_static_ct_entries_parsed | counter | Entries 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
Every Chrome- and Apple-trusted Certificate Transparency log, with cross-log deduplication.
Argon, Xenon
Cloudflare
Nimbus, Raio (static CT)
DigiCert
Wyvern, Sphinx
Sectigo
Elephant, Tiger, Mammoth, Sabre
Let's Encrypt
Willow, Sycamore (static CT)
TrustAsia · Geomys · IPng
HETU, Luoshu, Tuscolo, Halloumi, Gouda