GitHub

Certificate Transparency streaming in Rust

Watches the Chrome- and Apple-trusted Certificate Transparency logs and streams every new SSL/TLS certificate to you over WebSocket or Server-Sent Events. RFC 6962 and static-ct-api logs both work, in one binary.

Run
$ docker run -d -p 8080:8080 \
    ghcr.io/reloading01/certstream-server-rust:latest
Stream /domains-only
$ wscat -c ws://localhost:8080/domains-only
{"message_type":"certificate_update",
 "data":["example.com","www.example.com"]}

Features

WebSocket and SSE

Use WebSocket or Server-Sent Events, whichever your client prefers. Both carry the same certificates.

RFC 6962 and static-ct-api

Classic get-entries logs and the newer checkpoint-and-tile static-ct-api logs are watched side by side in one process.

Cross-log deduplication

One certificate usually ends up in several CT logs. The server hashes each one with SHA-256 and sends it once per deduplication window, so your client does not see repeats.

State persistence

Log positions are saved to disk. After a restart or an upgrade each log continues where it stopped instead of starting over.

Connection limits

Optional caps on total connections and on connections per IP address.

Token authentication

Turn on bearer tokens for clients. Tokens are compared in constant time. You can set several of them and change the header name.

Configuration reload

The config file is watched. Settings that support it take effect without a restart.

Log health

Every CT log has its own watcher. Repeated failures move it from healthy to degraded to unhealthy, with retries, exponential backoff and a circuit breaker per log.

Metrics and REST API

Prometheus metrics at /metrics. An optional REST API returns server statistics, CT log status and certificate lookups.

Compatibility

The message format is the one Calidog's certstream-server and certstream-server-go use, so a client only needs a different WebSocket address.

Python certstream library
import certstream

certstream.listen_for_events(callback,
    url='wss://your-host:8080/')
Anything else plain WebSocket
# same certificate_update messages,
# same all_domains, same leaf_cert
wscat -c wss://your-host:8080/

Message types, field names and the leaf_cert shape are unchanged, so a parser written for either of them keeps working. On top of that, this server reads static-ct-api logs and drops duplicates seen across logs. If you only want domain names, /domains-only sends about 100 to 500 bytes per message instead of 2 to 5 KB.

Performance

Two hours against all 45 Chrome- and Apple-trusted logs, default configuration, one subscriber on the domains-only stream, metrics sampled every 30 seconds.

420/s
Certificates ingested, median. 796/s at p95
722/s
Domain names, 62 million a day
80MB
Resident memory, steady state. 50 MB allocated
0.2core
CPU, median

At the median that works out to roughly 36 million certificate updates a day. Each certificate is serialized once and shared with every subscriber through an Arc<PreSerializedMessage>, and with no clients connected the serialization is skipped. Memory is covered in the API docs.

Support

I develop certstream-server-rust in my spare time and release it under the MIT license. If it is useful to you, a star, a bug report or a word to someone who needs it helps.

GitHub Sponsors is there if you want to support the work.

Sponsor on GitHub