The /explore page on this site is a live view into telemetry the site collects about itself. The writing pages are static HTML and use no database. The dashboard is a separate application backed by Supabase, where three ingestion streams feed public aggregate views. Refresh the page and it reads the latest completed ingestion runs.
This article is the legend for that page. If you landed here from /explore wondering what HP, CF, UM, and CMP mean, this is the answer. For the architecture underneath, see the companion colophon: which repos run which streams, why a Cloudflare Worker stitches them together, and how the page rolls back when something breaks.
Why I Run Production-Grade Personal Infrastructure explains why the site exists at all. About this site maps the system behind it. This piece explains what the site sees.
Why publish it
Most security and analytics telemetry stays private because the systems it describes have to stay private. The infrastructure I manage professionally is not something I can write about in specifics. The infrastructure I run for myself is. So when I argue that the gap between professional and personal practice is where blind spots form, the consistent move is to open the personal side.
The page is small. Three streams plus a few composite tiles, a few dozen tiles total. But it is honest about what is observable from a personal site running behind Cloudflare, with a small honeypot attached, and a self-hosted analytics beacon. One detail is worth flagging up front. The persistent bottom of the honeypot's long tail is /wp-login.php, and this site has never run WordPress. Nothing about that traffic cares. The probes never stop on a domain that has nothing to find.
What the streams show
Each stream covers a multi-week window. The honeypot has been running since the current deploy on April 15, 2026. Its window grew with the deploy clock and stood at 28 days when these figures were taken, on 2026-05-13; the tables below are that snapshot, and the honeypot has since moved to the same thirty-day rolling window as the other two streams. The Cloudflare and Umami streams are thirty-day rolling. The numbers as of writing: 30,100 honeypot events, 618 source addresses, 72 countries.
The Cloudflare stream covers thirty days of edge traffic. 54,723 requests, 4,396 blocks across 85 countries. Block in this dataset means a managed WAF rule, a custom rule, or a rate limit triggered. The longer-term plan is to push these logs into BigQuery using the same pattern that already runs for another project. Until that is wired, the numbers come from zone analytics directly.
The Umami stream covers thirty days of analytics on the site itself: 392 pageviews, 117 visitors, 164 visits, 106 bounces. Umami is self-hosted, runs on a Mac Studio in a closet, and is privacy-respecting by design. Running Google Analytics on a site that argues against pervasive tracking would be incoherent.
There is one detail that sits oddly until you know what it is. The number-two path on the honeypot table is /pixel.gif, and it is mine. It is the analytics beacon Umami fires on every page render. The honeypot classifies it as a canary. Filtering it would mean trusting my own classifier before showing the data. Better to leave it visible and explain it. The number-one path is /xmlrpc.php at 2,065 hits, a WordPress endpoint on a site that has never run WordPress. More on that in Table 2.
How to read it
Tiles on /explore are coded by source so they are never ambiguous. HP is honeypot. CF is Cloudflare. UM is Umami. CMP is composite, meaning a tile that joins more than one source. Numbers are Postgres views. They reflect the source tables at the moment you loaded the page.
The /explore page is not a product. It is a window into the same infrastructure the companion piece describes, run for the same reasons.
From edge to view in three hops
The data path has three segments and one aggregation point.
Edge. Cloudflare sits in front of the site as the WAF and the CDN. Zone analytics expose request counts, blocks, and country distribution through the API. There is no logpush yet. That is a separate workstream that retasks an existing Cloudflare-to-BigQuery pipeline running on another project.
Honeypot. The Cloud Run service classifies requests that have no legitimate route and publishes security events to Pub/Sub, which writes them to BigQuery. The record includes request metadata used for threat analysis; the public dashboard receives only a redacted projection.
Analytics. Umami runs self-hosted on a Mac Studio at home behind a Cloudflare Tunnel. It writes to its own Postgres on the same machine.
Aggregation. Three scheduled Edge Functions pull from BigQuery, Cloudflare, and Umami into private Supabase tables. Public views wrap those tables with the joins, filters, and time windows the dashboard needs. The honeypot poller redacts source IPs in BigQuery before upserting them into Supabase. Code:
The current top-path view reads the poller-fed 30-day table in the private schema. The public surface is an owner-executed aggregate view; browser roles cannot query the underlying rows.
CREATE VIEW public.honeypot_top_paths AS
SELECT request_path,
count(*) AS hit_count,
count(*) FILTER (WHERE is_canary_trigger) AS canary_hits,
count(*) FILTER (WHERE is_trap_endpoint) AS trap_hits,
count(DISTINCT source_ip_redacted) AS distinct_sources,
max(timestamp) AS last_seen
FROM private.honeypot_events
WHERE is_canary_trigger OR is_trap_endpoint
GROUP BY request_path
HAVING count(*) >= 2
ORDER BY count(*) DESC
LIMIT 50;
The /explore page calls these views directly. Ingestion runs on a schedule, while aggregation happens at read time because the retained volumes are small and Postgres is indexed for the queries the page makes.
Honeypot stream (HP codes)
At publication, the available honeypot window covered April 15 through May 13, 2026: 28 days. The current pipeline retains a rolling 30-day window across deployments; the values below remain the verified publication snapshot.
Table 1. Honeypot summary, publication snapshot.
| Metric | Value |
|---|---|
| Events | 30,100 |
| Distinct source IPs | 618 |
| Countries | 72 |
| Canary hits | 422 |
| Trap hits | 2,576 |
The public surface distinguishes canaries from traps. Canaries are deliberately believable decoy routes with five response types: an environment file, AWS credentials, Git configuration, a database backup, and the tracking pixel. Traps are paths the site never serves and that no legitimate client should request. Trap hits are what you actually want to look at.
Table 2. Top trap paths, current window. Paths the site never serves, where every hit is a probe.
| Path | Trap hits |
|---|---|
| /xmlrpc.php | 2,065 |
| /wp-login.php | 68 |
| /admin | 24 |
| /wp-admin/ | 22 |
| /wp-admin | 15 |
| /wp-admin/css/bolt.php | 11 |
| /wp-admin/css/ | 10 |
| /wp-admin/js/index.php | 10 |
The shape of this table is the strongest version of the article's thesis. /xmlrpc.php at 2,065 hits on a site that never ran WordPress dominates the distribution. WordPress paths occupy seven of the top eight slots. Credential paths like /.env, /.git/config, and /.aws/credentials also see steady traffic but are classified as canary, not trap, because they trigger a different handler. The probes never check whether the target software is actually present.
The original snapshot was deploy-bounded. The current poller queries and retains a rolling 30-day window, so redeploying the site no longer resets the dashboard's honeypot history.
Cloudflare stream (CF codes)
Thirty-day rolling window, pulled from zone analytics by the cf-poll Edge Function and stored in Supabase.
Table 3. Cloudflare summary, last 30 days.
| Metric | Value |
|---|---|
| Requests | 54,723 |
| Blocks | 4,396 |
| Countries | 85 |
| Block rate | 8.03% |
A block in this dataset is any of three things: a managed WAF rule firing, a custom rule firing, or a rate limit triggering. The aggregate hides the breakdown today. A future iteration of the view will split it.
The Cloudflare side of /explore is the stream that prompted Article 23. When the request count tripled week over week from the same set of source countries, having a public view of the data made the analysis easy to share. Before that view existed, the data lived in a tab in the Cloudflare console.
The longer-term plan is logpush to BigQuery, which gives full request-level granularity rather than zone-level rollups. The pattern already runs for an unrelated project. Retasking it is an open workstream.
Umami stream (UM codes)
Thirty-day rolling window. Self-hosted Umami on a Mac Studio behind a Cloudflare Tunnel.
Table 4. Umami summary, last 30 days.
| Metric | Value |
|---|---|
| Pageviews | 392 |
| Visitors | 117 |
| Visits | 164 |
| Bounces | 106 |
The choice to self-host is consistent with the position taken in Why Web Tracking Survives and Web Tracking Techniques: A 2026 Field Guide. Running a third-party tag on a site that argues against pervasive tracking would not survive a careful reading.
Naming codes (CMP)
Tiles on /explore are prefixed with the source code so the page reads cleanly without a per-tile legend. HP for honeypot, CF for Cloudflare, UM for Umami, CMP for composite. Composite tiles answer questions a single source cannot, like countries that appear at the edge as blocks and also appear in the honeypot table within the same window. They are joins across source schemas, not aggregates.
What is not published
Source IP values never reach a public view. The Supabase copy is truncated to /24 for IPv4 and /48 for IPv6 before ingestion, while public views expose only aggregates such as country and distinct-source counts. No request bodies, headers, or cookies appear on the public surface. The one string that does is the user agent: the attack view lists the most common hostile user agents, which identify tools, not people. No data from any system managed in a professional capacity is included.
BigQuery retains the private forensic event stream. Supabase stores a narrower, redacted 30-day projection for the dashboard, and public views reduce it again to the fields and counts each visualization needs.
What is next
Three things are queued.
The Cloudflare logpush to BigQuery is the largest. Once that lands, the CF tiles move from zone-level rollups to request-level data with hourly resolution. The pattern is borrowed from the personal-site work documented in 03.5 and the Article 23 cyber-spillover analysis - both of those articles ran into the same data-availability ceiling.
The honeypot stream has since moved to a true rolling 30-day window across deployments. Its Edge Function ingestion path also replaced the original in-database FDW refresh, making external-query failures observable without tying up Postgres workers.
The composite views are the third item. Today CMP is one tile. The list of useful joins across the three sources is longer than that, and the next iteration of /explore will work through them.
The code that builds /explore lives in its own repository, separate from the site. Neither is public; the architecture is documented in Why I Run Production-Grade Personal Infrastructure and the companion colophon. The README is honest that it is not designed for reuse. It is designed to keep the data legible to me, and to anyone who wants to see what a personal site sees.