Home server
status-publisher reads uptime and container counts
GitHub Gist
one public JSON file, six fields
This site
reads the file, shows the last known values
The problem
The portfolio claims the home server keeps running. That claim is either backed by live data or it isn't. The obvious approach, an endpoint on the server that the site calls, requires a public IP, port-forwarding on the router, and a service on the home network reachable from the internet. That is a larger attack surface than the information is worth.
The design
The publisher runs as a Docker container on the server. Every 15 minutes it reads two things locally: uptime from /proc/uptime and container counts from Docker. It then makes one outbound HTTPS call to the GitHub Gist API and overwrites a single JSON file.
The portfolio fetches that file. The data goes in one direction only: server → GitHub → site. The publisher never accepts a connection; it only makes them.
What is deliberately not published
The JSON contains six fields: a timestamp, uptime in days, how many containers are running, how many exist in total, and two optional incident dates. That is all.
No hostnames. No IP addresses. No container names. No file paths. No service versions. An attacker reading the snapshot learns that some number of containers are running on some machine somewhere. They learn nothing that helps them find or reach that machine. The sanitization is not cosmetic: it is the security model.
Docker access without the keys to the server
Counting containers needs the Docker API. The usual shortcut is mounting the Docker socket read-only. That :ro protects the socket file, not what you can do through it: any process holding the socket can start a privileged container, which is root on the host.
So the publisher never sees the socket. It talks to a small proxy on an internal network with no route to the internet. The proxy allows exactly one request, GET /containers/json, and only from the publisher. I tested that list instead of trusting it: the container list returns 200; inspecting a container (which would expose other services' environment variables, and with them their passwords), reading logs, listing images and creating containers all return 403 or 405, and so does the allowed request from any other container.
Why push, not pull
The server already serves a few things to the internet through a reverse proxy with TLS, each one a deliberate choice, kept patched and behind authentication. A status endpoint would be one more of those, for data that does not need it.
Push adds nothing to that surface. The publisher makes one outbound HTTPS call to a known host and accepts no connections at all.
If the publisher crashes or the server goes offline, the portfolio displays the last known snapshot with a 'last updated' timestamp. A stale widget, not a broken one.
What I would do differently
The GitHub token has gist scope only, which limits blast radius if it leaks. What it does not do is alert me if it does leak. I would add token rotation on a schedule and a check that the published content has not been tampered with: a hash of the expected shape signed with a key that never leaves the server.