patrick@fribourg:~$ cat dashboard.md
A dashboard that stays home
Your numbers, on your own network, and nowhere else. Your sites' audience and your App Store figures on one page made for a phone — no analytics service, no account, no third party. I built it for myself; it turned out to be worth sharing, so here it is, under an MIT licence.
cat what-it-is.md
Example data. Your own stays on your machine.
A single page you add to your home screen. It reads your nginx logs and Apple's sales reports, and tells you what changed since yesterday — not what happened in general. Every section is optional: give it one site and nothing else, and it shows one site.
- Site audience — human visitors per day, most-read pages, devices. Robots, scanners and rented-server addresses are set aside rather than counted as readers.
- App Store — first downloads, updates, in-app purchases, revenue per currency, version history, TestFlight groups.
- Indexing — which crawlers come by, and whether they ever open the one page you care about.
- Mastodon — followers, posts, the latest few.
It is served by an nginx bound to 127.0.0.1 and
opened to your Tailscale network alone. Not published, not password-protected: simply
unreachable from the internet.
The rules matter more than the figures. Two sources that contradict each other are worth nothing — so the dashboard says, in writing, how it counts a visitor and where a day begins.
cat install.md
Three commands. The first builds a single-file installer from the sources; the second shows you what it would do and writes nothing; the third does it. Python 3.9 or later, and that is the only hard requirement — Docker, Tailscale and Pillow are each optional, and the installer says so rather than failing.
git clone https://github.com/pat-the-geek/private-dashboard.git cd private-dashboard # builds installeur-tableau-de-bord.py from the files you just read python3 scripts/faire-installeur.py # shows every step it would take, and writes nothing python3 installeur-tableau-de-bord.py --vers ~/my-dashboard # does it: files, icon, figures, container, private access, morning task python3 installeur-tableau-de-bord.py --installer --vers ~/my-dashboard
Then one file to fill in. Everything in it is optional: drop a section and its part of the page simply disappears.
{ "titre": "Dashboard", "marque": { "haut": "AB", "bas": "cd" }, // the two halves of the icon "accent": "#E8972E", "jours": 30, "fuseau": null, // IANA zone; absent, the machine's own "sites": [{ "nom": "example.test", "journaux": "~/sites/example/logs/access-*.log", "classement": null, // your own human/robot classifier "page_surveillee": "/notes.html" // warn me if crawlers skip it }], "appstore": { "env": "~/.appstore/ventes.env", // a Sales-role key; the .p8 stays put "apps": [{ "id": "0000000000", "nom": "My app", "sku": "test.example.myapp" }] }, "mastodon": [{ "compte": "@you", "instance": "https://your.instance" }] }
And the part worth doing carefully — opening it to your own network, and to nothing else:
tailscale serve --bg --https=443 http://127.0.0.1:8090 # three checks beat one promise dig +short your-machine.your-tailnet.ts.net @1.1.1.1 # must answer NOTHING tailscale funnel status # "tailnet only" lsof -nP -iTCP -sTCP:LISTEN | grep 8090 # 127.0.0.1 only
⛔ Never tailscale funnel: it would publish these
figures on the internet.
cat read-before-you-run.md
Nobody owes you their trust
The installer is one file of about 130 kB, most of it base64. Running that on a stranger's word is a bad idea, and I am a stranger. So the sources sit in the open and the installer is rebuilt from them, by you, in one command.
Never two copies
The public repository is not a hand-kept copy: a job writes it from the real installer package every time the source changes. What you read is exactly what gets written on your machine — not a neighbouring version that drifted.
The build refuses to leak
My own sites, apps and machine names are listed in a file that stays private, and the build fails if any of them survives in what ships. It is proof rather than a promise — it has caught real mistakes.
cat limits.md
Three things it cannot do, said here rather than discovered later. Install sources — App Store search, browse, web referrer — need Apple's Analytics API, so an Admin-role key and a report request filed in advance. Search terms typed in the App Store exist nowhere but Apple Search Ads. Build numbers are out of reach of a Sales key. And without a real classifier script, telling humans from robots is a coarse guess that a disguised crawler will walk straight through.
Read it first, run it second. And if your organisation is wondering what to do with AI without losing its people or its data, that is my day job.
github.com/pat-the-geek/private-dashboard →