For all of us homelabbers and tinkerers — I built Tempo, one Mac timeline for what your setup is doing
For all of us who run stuff at home — homelabbers, self-hosters, tinkerers — I built Tempo: a small, native Mac app (~13MB) that pulls what your setup is doing onto one live timeline. The problem To know what's actually going on, you've got Home Assistant in one tab, an uptime monitor in another, backups somewhere else, the network gear's UI buried in a bookmark. You end up opening five things just to confirm everything's fine. Tempo pulls those events onto one timeline, newest first. A backup finished. A host went down and came back. An automation fired. Something changed in Home Assistant. The output of your own scripts, ready to read. It's all just there, in one place. Every event carries a set of buttons — build them in Tempo's visual Score Editor, or in the score's JSON if you prefer — so a click does whatever you need it to. And not just opening a link: run a command or a whole script, SSH the host that dropped, copy the exact value out of the payload, mark a reminder done — you define what each one does. Tempo doesn't act on its own, but your clicks can do plenty. It's not another control panel to babysit — it's the thing that tells you whether you even need to open the others. The comparison The closest things are status dashboards — Homepage, Heimdall, or a Grafana board. They're good, and they answer a different question. A dashboard shows the current state: is it up, how full is the disk, right now — you pull it up in a browser tab and read the gauges. Tempo shows what happened: the backup that finished overnight, the host that dropped and came back, the automation that fired while you were out. Newest first, on one native timeline you glance at — not a tab you keep parked. Your dashboard tells you the state; Tempo tells you the story, and hands you the next click. Before: five tabs to confirm all's fine. Now: one glance. Pricing Free. Tempo v1 is freeware — no account, no paywall. The download from the site auto-updates itself; via the Homebrew tap you update with brew upgrade. Download: tempoapp.app What it pulls from right now: Home Assistant, Uptime Kuma, Beszel, Kopia, UniFi, Pi-hole the media stack — Jellyfin, Sonarr/Radarr/Prowlarr, Jellyseerr Vaultwarden, GitHub Actions, Shortcuts, webhooks, Hazel And you're not limited to the built-in sources. Anything you can script can become an event: a shell script watching filesystem usage, SMART temperatures, a custom backup's exit status, Cloudflare stats — it runs, emits a line, and lands on the timeline like everything else. If you can write it, Tempo can show it. Optional agenda panel at the top — Apple Calendar & Reminders, Todoist. Your day at a glance, but not a calendar: read-only, kept above the timeline, never mixed into your infra feed. Turn it off and it's gone. Two integrations ship experimental for now: Synology (built from the vendor docs, not yet tested on real hardware) and CalDAV (Fastmail only, limited features). Feedback welcome. More integrations are on the roadmap, and I'm happy to add ones that aren't — but you don't have to wait for me. Because any script can be a source, the community can build and share their own: the scores that describe an integration are just files you can pass around. Tell me what you're missing, or bring what you've built — that's how this grows. The basics: Native, ~13MB, macOS 15+ Local-first — it talks to your services over your LAN. No cloud, no account, no telemetry. Signed, notarized, and free. If any of that sounds like your setup, it's for you. If none of those names mean anything to you, this one's probably not — and that's totally fine. Trust & transparency (r/macapps Tier 2) Developer: LinkedIn profile Privacy Policy: https://tempoapp.app/privacy/ Terms / License: https://tempoapp.app/license/ How it's built (architecture & privacy notes): https://tempoapp.app/blog/architecture-and-security/ Blog: https://tempoapp.app/blog/
已收集讨论
hi, dev here. Just wanted to say Tempo was built for me and my own homelab — the tools I connected are the ones I actually run and test against day to day. My Synology died right before I could test that one, so its integration is built from the official docs and ships marked experimental until I can try it on real hardware. The roadmap has a few more I'll be testing soon, plus some I'm honestly not sure I can test properly myself — my hope is a community grows around it and we add more and more sources together (the scores are just files, so anyone can write and share one). What would you wire up first?
great name, almost like https://www.tempo.io or https://grafana.com/oss/tempo/
haha yeah, kinda figured that out too late 😅 apparently everyone in dev tooling wants that name. no relation to either though, mine's just a little native Mac app that throws your whole homelab's events onto one timeline. no tracing backend, no Jira timesheets, I promise. good company to end up in by accident though 😄
I'm seeing these errors in Homebrew: ✘ Cask tempo (1.1,25) Error: Download failed on Cask 'tempo' with message: Download failed: https://downloads.tempoapp.app/Tempo-1.1.dmg curl: (56) The requested URL returned error: 403
Thanks for the report, and sorry for the hassle. Good news first: it isn't the DMG or the cask, the file is public and downloadable. From my end every client gets a clean 200, Homebrew's user agent included: ``` curl -sI https://downloads.tempoapp.app/Tempo-1.1.dmg # HTTP/2 200 curl -sI -A "Homebrew/4.4.0 curl/8.6.0" https://downloads.tempoapp.app/Tempo-1.1.dmg # HTTP/2 200 ``` So the 403 you hit is per-requester, not a blanket outage. My best guess is Cloudflare's Bot Fight Mode on the download host: it scores incoming requests and 403s some automated download clients (Homebrew/curl) by IP, so it catches some people (datacenter, VPN, certain ISPs) and lets others straight through. Classic Homebrew-behind-Cloudflare gotcha, and it's a CDN config thing, not the app. I'm turning that protection off for the downloads domain now. In the meantime the direct browser download works fine: https://tempoapp.app/downloads (only the Homebrew path is tripping the check). If you want to confirm it's IP-specific, run `curl -I https://downloads.tempoapp.app/Tempo-1.1.dmg` from your machine. If that 403s too, it's your IP getting flagged. I'll reply here as soon as the rule is off so you can brew install cleanly.
Thanks again for the report. I found the culprit: Cloudflare's Bot Fight Mode on the download host was returning 403 to some automated download clients (Homebrew/curl) based on the requester's IP, so it caught you but not everyone. Classic Homebrew-behind-Cloudflare gotcha, and it's a CDN config thing, not the app. I've turned it off. For what it's worth, the cask fetch runs clean and verifies the 1.1 build from our end: ``` ❯ brew update && brew fetch --cask --force caereforge/tap/tempo ==> Updating Homebrew... Updated 2 taps (caereforge/tap and homebrew/core). ... ✔︎ Cask tempo (1.1,25) Verified 13.3MB / 13.3MB ``` I can't reproduce your exact block from here, though, so you are really the one who can confirm it. Could you give it another go? brew reinstall --cask tempo (or a plain install if you removed it.) This might well have sorted it for you, but I would rather hear it from you than assume. Thanks for the patience.
nice work man congrats, u say local-first, LAN-only and how does it actually talk to each service? Polling APIs on an interval, or are some integrations (like webhooks) push-based? Curious about the load on something like a Pi running Uptime Kuma.
Thanks! Tempo itself is push-based. It runs a small local ingestion server on your LAN and sources POST to it when something happens, it never polls anything on its own. Most integrations are native webhooks: Uptime Kuma, Home Assistant, UniFi Network and Protect, Kopia, Synology, Jellyfin. For Kuma you just add a Tempo notification, one webhook per monitor state change, not per check, so on a Pi it's negligible. A handful of services that don't send Tempo-shaped webhooks ship a small (provided) companion helper instead: a script you run next to the service that polls it, tails its log, or relays its events, then POSTs to Tempo. Beszel and Pi-hole use a small poller, Vaultwarden tails its log, GitHub Actions uses a relay. Tempo never installs or runs those itself, they live on your box, so any polling stays a lightweight per-integration thing on the service side. Apple Calendar and Reminders are the one thing Tempo reads directly, via EventKit locally on the Mac, event-driven too. If you want the full source-by-source breakdown of what's a native webhook vs a helper, it's all here: https://tempoapp.app/docs/10-sources-reference/
Is this like Dozzle / Beszel but in a desktop app?
Not really the same thing. Dozzle and Beszel show current state, live logs and resource metrics. Tempo is a timeline of events instead: a backup finished, a host went down and came back, a cert is expiring... And each event can carry some action buttons you can customize. It's complementary more than a replacement, it can even take events from Beszel (as I do). And yes, native Mac app, not a web dashboard.
Cool I’m gonna give it a spin. Would you consider that adding that live view? I could see a Mac app that allows me to get notifications (driven by dozzle logs, apprise API, etc) + do some basic troubleshooting (look at logs, look at resources, start / restart containers) being very helpful. Even if you just added a button for Dozzle and Beszel and opened the web interface in app, I don’t think you need to reinvent the wheel.
Not a hardcore homelabber but the timeline idea is great. does it capture app installs/updates and login items too? A "what changed on this Mac and when" view would've saved me hours
Funny you ask, I actually do the installs part myself. New apps come in through Hazel plus RapidMG (it handles the DMG-to-Applications install), and I just tacked a small script onto the end of that so it posts a line to Tempo. A fresh install shows up on my timeline on its own. Updates aren't part of that pipeline for me, those go through each app's own updater, but the same trick works anywhere you can run a script: a one-liner where your updates happen, or a small script that checks login items, and it lands on the same timeline. The generic Scripts source is built for exactly this. So it's DIY rather than automatic out of the box, but it's just a couple of lines wherever the change already happens. Here's the actual Hazel rule and the little script it runs: The script hazel-dmg-temp.sh: ``` #!/bin/zsh # Hazel companion: when a DMG lands in the auto-install folder (opened with # Rapidmg), post a "DMG installed" event to Tempo. # export PATH="/usr/bin:/bin:/usr/sbin:/sbin:$PATH" # Hazel runs with a minimal env PROVIDER="com.noodlesoft.hazel.installs" # shows as Hazel > Installs in Tempo RULE="Auto-install DMG (Rapidmg)" dmg="$1" [ -n "$dmg" ] || exit 0 name="${dmg:t:r}" # filename, no path, no .dmg extension TOKEN=$(/usr/bin/security find-generic-password -l "Tempo: Hazel" -w 2>/dev/null) [ -n "$TOKEN" ] || exit 0 esc() { local s="$1"; s="${s//\\/\\\\}"; s="${s//\"/\\\"}"; printf '%s' "$s"; } title=$(esc "Installed $name") path=$(esc "$dmg") rule=$(esc "$RULE") /usr/bin/curl -s -m 5 http://127.0.0.1:7776/ingest -H "X-Tempo-Token: $TOKEN" -H "Content-Type: application/json" -d {\"title\":\"$title\",\"providerIdentifier\":\"$PROVIDER\",\"eventType\":\"alert\",\"severity\":\"ok\",\"metadata\":{\"path\":\"$path\",\"rule\":\"$rule\"}}" >/dev/null ```
Looks like a great app...Thanks
Thanks, that means a lot. Hope it's useful, and give a shout if you run into anything or need a hand configuring a source, that's usually the fiddly part.
Thanks my friend
At first, I thought this was a metronome app because of the name. Very nice app!
Ha, I can totally see why! And funnily enough the musical theme runs deeper; there are Scores too, which let you define how Tempo reacts to events and what actions to trigger. It's actually intentional. In my head Tempo was born exactly from that idea: the tempo is set by my infrastructure, and the Scores are what interpret it. The events are the beats, the Scores are the sheet music. 🎵 Thanks for the kind words!
ohhh, gonna save this for later, it looks great! I'm in the early stages of getting lots of gadgets for my new house so hope to take it for a spin soon
Thanks, that genuinely means a lot! A new place full of gadgets is honestly the perfect excuse to take it for a spin, that's exactly the kind of setup Tempo is happiest in. No rush at all, but whenever you get to it and want a hand wiring something up, I'm around and happy to help: hop into the Discord (discord.gg/QnaCNKd9CN), DM me or just post publicly, whatever's easiest... or drop me an email at support@tempoapp.app. Enjoy the new house! 😄
Does this work with tailscale "for free" so my couple servers in a data center could ping my local mac behind a firewall without opening anything?
Yep, works great over Tailscale. Tempo listens on the local network (not just loopback), so your data-center boxes just hit your Mac's tailnet IP and POST away. Nothing exposed to the internet, no ports to open, no firewall fiddling. And free Tailscale is more than enough for this. In my case everything comes in through my Tailscale subnet router, so all the POSTs land from that one node's IP instead of each server. No big deal though: Tempo's per-token allowlist takes a CIDR, so I just whitelist the whole tailnet /24 and call it a day. Oh, and that same tailnet also lets me trigger my Shortcuts from my phone (Apple Pay, geofencing, all that fun stuff). And if you get stuck or just want more info, feel free to ping me anytime (DM, email, or Discord, whatever's easiest).
这条评论已在来源处删除。
Absolutely — Tempo runs its own local SQLite database and keeps the full event history for as long as you want. Retention defaults to Forever; you can also cap it (30 days up to 2 years) and it prunes permanently at that horizon. One heads-up: pruning below ~84 days blanks the activity heatmap (it's a 12-week view, so it needs that much history to draw), so keep retention at 84 days or more if you rely on it. The timeline caps how much it holds in memory at once for responsiveness — and that window is yours to tune in Settings (how many days to keep in the live feed, plus an optional event-count cap) to match your machine's memory and performance. It's just the view, though: everything stays in the db, and older events lazy-load as you scroll back, so scrolling a local timeline without standing up Grafana/Prometheus is exactly what it's built for. There's also a Search tab that queries the db directly (not just what's on screen): scope it to one or more sources, filter by state, type, date range, and match on title, subtitle, tags, or metadata. It works over the most recent 5,000 events; plenty for day-to-day lookups, and a searchable external archive (to query the whole history, not just the recent window) is on the roadmap. And for the complete record right now, any source (or a whole umbrella of them) has a "Save full history…" action that exports its entire timeline to CSV or JSON, as far back as your retention keeps it.