There is a category of software nobody ships, nobody markets, and nobody reviews: the software you build for yourself. An audience of one. The community has started calling it personal software, and I think it’s about to become the most interesting category there is.
This is the story of mine. It’s called data, it fits on one dark page, and it shows the traffic of all my projects side by side.
The itch
I run several small products, Posture, StoreScreenshot, Keto Life, my portfolio, and the list keeps growing. Each one sends its events to PostHog, which does an outstanding job of collecting everything: pageviews, custom events, session replays, geolocation, the works.
But every morning, checking on them meant opening one PostHog project per product, as many dashboards, and a UI built to serve every company on Earth. PostHog has hundreds of features. I use maybe twelve. The other ones aren’t a bonus, they’re noise between me and my numbers.
What I actually wanted fits in one sentence: one private page, all my projects, visitors per day, where they come from, and who they are.
So I built exactly that, and nothing else.
The shape of the solution
The architecture is almost embarrassingly small:
PostHog Cloud (the brain: every event, every replay)
▲
│ HogQL over HTTPS, personal API key
│
Hono server (one Node process, bundled to a single file)
▲
│ JSON, aggressively cached
│
React + Vite (the face: one page, all my projects)
PostHog is the brain. It keeps doing what it does best: capturing everything reliably, storing it in ClickHouse, exposing it through HogQL, their SQL dialect. I didn’t rebuild a drop of analytics infrastructure.
My app is only the face. A small Hono server asks PostHog questions like this one:
SELECT toString(person_id) AS personId,
min(timestamp) AS firstSeen,
max(timestamp) AS lastSeen,
count() AS pageviews,
argMax(properties.$geoip_country_name, timestamp) AS country
FROM events
WHERE event = '$pageview'
AND timestamp >= now() - INTERVAL 7 DAY
GROUP BY personId
ORDER BY lastSeen DESC
…and a React page renders the answers the way I like them: dark, dithered pixel charts, zero configuration screens.
What about security?
The dashboard has no authentication at all, and that’s a deliberate choice, not an oversight. It’s made possible by one decision: the app only exists inside my tailnet.
A tailnet is the private network Tailscale builds between your devices: my laptop, my phone and my server hold WireGuard keys and talk to each other over an encrypted mesh, whatever network they’re on. The dashboard binds to the server’s tailnet address, never to a public one. From the internet’s point of view, there is no port to scan, no URL to guess, no login page to brute-force. The service simply does not exist.
That single property replaces the entire login/permissions/settings layer that eats half of every real product’s codebase. A product must authenticate strangers; my dashboard only ever answers devices I have personally enrolled. Personal software gets to cheat like that.
Two more lines of defense, in case a device on the tailnet were ever compromised. The PostHog API key lives on the server and never reaches the browser, which only receives aggregated numbers. And every value interpolated into a HogQL query is strictly pattern-checked first, so even a malicious client on the network could not turn a query parameter into an injection.
The part that changed: the cost of a feature
This tool is never finished, and that’s the point. Every time a question crosses my mind, I describe it to an AI agent, and the view exists shortly after. Not a mockup: shipped, typed, verified against production data.
One real day with it, this week:
- “I’d like a quick way to see my latest visitors, fifty per page.” → An hour later: a paginated visitor list. The agent even discovered along the way that PostHog forbids
OFFSETon personal API keys, and silently switched the implementation to keyset pagination. - “Can I click an event and see who triggered it, with which link?” → Same afternoon: a full-screen modal, sliding up from the bottom like the overlay on this very portfolio, listing every occurrence with its visitor and its custom properties.
- “Why do my visitors show as direct?” → The agent dug through the dashboard, my portfolio’s code, and the git history, and found that my product links carried
rel="noreferrer", and that the UTM tags were one day old.
A year or two ago, each of those would have been an evening I didn’t have. The backlog would have grown, the tool would have stagnated, and I would have crawled back to the general-purpose UI. In the agentic era, my analytics tool grows at the speed of my curiosity.
Why private beats general-purpose
A general-purpose tool has to serve everyone, so it must carry every feature anyone might need, behind menus deep enough to hold them all. Personal software inverts the deal: it does exactly your job, with a surface so small you can read all of it in one sitting.
And there’s a quieter benefit. This dashboard knows things I’d rather keep to myself: which product is growing and which one isn’t. It runs on my machines, on my tailnet, for me. Private software for private data.
Conclusion?
The interesting move wasn’t building a dashboard. It was noticing that the build-vs-buy equation flipped when the cost of building collapsed.
PostHog stays, because collecting and storing events reliably is genuinely hard, and they’re excellent at it. Everything above it, the part that’s just my preferences projected onto my data, is now cheaper to own than to configure.
I already wrote it about my deploy scripts, and it’s even truer here: owning your tools means owning your time. Personal software is what that ownership looks like when it compounds.