Every certificate
for your domains.
Nothing you didn't expect.
Merkleye is a self-hosted Certificate Transparency watchtower. It tails the CT log firehose in real time, flags certificates from issuers you never authorized, and catches lookalike-domain certificates — often the earliest signal of phishing infrastructure going live — before anyone clicks a link.
- 20–30M
- CT log entries tailed per day
- 100k+
- lookalike domains watched per sweep
- 0
- third parties see your domain list
# cert observed on a CT log, seconds after issuance
domain: secure-paypaI.com ← homoglyph of paypal.com
issuer: Let's Encrypt (unexpected: not authorized)
source: oak2024h1.ct.letsencrypt.org
dedup: sha256(der) 8f21…a04c
match: dnstwist variant, eTLD+1 reduced
score: 87 / 100 (additive, explainable)
notify: slack, webhook → sent in 0.4sWhy Merkleye
Built around three things a watchtower can't compromise on.
Security
Self-hosted by design — your domain list and findings never leave infrastructure you control.
- No SaaS relay: run it on a homelab box or an enterprise cluster, same image.
- Apache-2.0 and auditable — the matching and scoring logic isn't a black box you have to trust blind.
- Config-as-code: config.yaml is the sole write authority in v1, so there's no UI/DB drift to reason about during an incident.
Speed
Firehose ingestion means detection latency is bounded by CT log propagation, not by how often you remember to query.
- Tails every RFC6962 and static-ct-api log continuously via a certspotter sidecar — no polling, no per-domain API calls.
- Every certificate is tested against your hash set as it's observed, not on a batch schedule.
- Notifications fire (SMTP, webhook, Discord, Slack) the moment a match clears policy — while the phishing infra is still being staged.
Accuracy
Tuned to keep the signal-to-noise ratio survivable: routine renewals stay quiet, real lookalikes don't slip through.
- Expected-issuer policy is a case-insensitive substring match on issuer DN/O — CA intermediates rotate silently, so exact match would page you on every renewal.
- Dedup keys on sha256(DER), never the certificate serial — serials collide across CAs and would under-count real issuances.
- Domains normalize to punycode/A-label before comparison, matching how SANs are actually encoded on the wire.
Architecture differentiator
Firehose ingestion, not query-per-domain.
Watching lookalikes at scale means watching permutations, not just domains: 20 real domains fed through dnstwist becomes roughly 100,000 lookalike variants to monitor. A query-per-domain API model needs 100,000 requests to sweep that set once. Merkleye doesn't query at all — it tails the CT log stream continuously via SSLMate's certspotter sidecar and tests every certificate it sees against your hash set in-line. That's what makes watching six figures of variants architecturally tractable instead of a rate-limit problem.
Global CT issuance runs roughly 20–30 million entries a day. Merkleye discards everything that doesn't match — only matches are persisted, so your storage footprint tracks your hit rate, not the internet's issuance volume.
Pipeline
From log entry to Slack message, one straight line.
No queue of domains to poll, no scheduled crawl. Every stage runs as certificates arrive on the log stream.
CT logs
Every RFC6962 and static-ct-api log, worldwide.
certspotter sidecar
SSLMate's certspotter tails each log continuously — no polling.
Normalize · dedup · match
A-label/punycode normalization, sha256(DER) dedup, PSL eTLD+1 reduction, hash-set match.
Policy & score
Expected-issuer substring check, transparent additive risk score.
Persist
Only matches land in Postgres — non-matches are discarded.
Notify
SMTP, webhook, Discord, Slack — deduplicated and rate-limited.
Observability included:OpenTelemetry tracing over OTLP/gRPC across every stage, ready to feed a Grafana · Prometheus · Loki · Tempo stack for enterprise deployments — or ignore it entirely on a homelab box.
Read the internals →Correctness
The details that keep false positives — and false negatives — down.
None of this is glamorous. All of it is the difference between a watchtower you trust and one you tune out.
Dedup on sha256(DER), never serial
Certificate serial numbers collide across CAs. Merkleye dedups on the SHA-256 hash of the DER-encoded certificate — the one identifier that's actually unique.
Punycode/A-label normalization
IDN homoglyph domains are Unicode, but SAN entries are A-label-encoded on the wire. Comparing Unicode against a wire-format SAN matches nothing and fails silently — everything is normalized to A-label before comparison.
Public Suffix List aware
Phishing certificates live on subdomains. Variant lookups reduce through the PSL to eTLD+1 first, so login.secure-paypaI.com still resolves back to the watched variant.
Transparent, additive scoring
Risk scoring (internal/score) is a documented, additive rule set — not a black-box model. Every score is explainable back to the specific signals that produced it.
Self-hosting
One container image, homelab to enterprise.
Same image whether it's a single container next to a Postgres instance on a homelab box, or a cluster deployment with an OTel collector and a full Grafana stack behind it. Configuration is code:config.yaml is the sole write authority in v1 — deliberately, to avoid drift between a config file and a database or UI.
services:
merkleye:
image: ghcr.io/wesleykirkland/merkleye:latest
restart: unless-stopped
volumes:
- ./config.yaml:/etc/merkleye/config.yaml:ro
depends_on:
- postgres
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: merkleye
volumes:
- merkleye-db:/var/lib/postgresql/data
volumes:
merkleye-db:domains:
- name: example.com
expected_issuers:
- "Let's Encrypt" # substring match on issuer DN/O
- "DigiCert"
watch_lookalikes: true
notify:
slack:
webhook_url: ${SLACK_WEBHOOK_URL}Ecosystem
Two repos, one watchtower.
The watchtower itself: CT log ingestion, normalization, policy engine, scoring, and notifications. Go, Apache-2.0.
View repository →A dedicated web UI for browsing findings, tuning policy, and reviewing risk scores — a separate project from this site.
View repository →Roadmap
- CT log ingestion
- Policy engine
- dnstwist lookalike pipeline
- Scoring & digests
- Auth & audit
- Web UI (merkleye-ui)