Appearance
Cron orchestration — Pi (front-door) vs Z4 Mini (brain)
project: hinata-infrastructuretype: decisionstatus: pending-hardwarecreated: 2026-05-26
Decision
Pi owns the cron schedule. Z4 Mini owns workload execution.
This is the home-lab equivalent of the Hinata Captain/Commander split: Pi acts as the always-on orchestrator (cheap, low-power, never sleeps), Z4 acts as the on-demand compute (heavier, only spun-up for jobs that need it).
Why
Always-on vs on-demand asymmetry. Pi 5 (≤15W) is designed for 24/7 operation at negligible cost (~£3/year). Z4 Mini (~45-65W idle) is too expensive to leave hot purely to wait for a 5-min cron tick. Cron must live on the always-on node.
Failure-domain isolation. If the Z4 reboots, updates, or fails, the cron schedule keeps firing — jobs queue or fail-fast, but the schedule itself is not lost.
Ifeanyi-counsel §1 + §10.1 topology. The 3-node split is already doctrinal: Pi = front-door / firewall / cron trigger; Z4 = brain (VMs + Ollama + heavy compute); jimmy-vps = public-facing API surface.
Mechanics
ConcernPi (cron host)Z4 (workload)
Schedulercron (system) or systemd-timer (units)Standard systemd services (one-shot for jobs, persistent for daemons) Trigger transportssh z4 'systemctl start hinata-poll-monzo.service' OR HTTP webhook to Z4's local API— Healthpi-health.sh heartbeat every 5 min → jimmy-vps /health/cron``z4-health.sh heartbeat → same endpoint Failure escalationPi unreachable for >10 min → Telegram alert via jimmy-vpsZ4 job failure → log to ~/Sandpit/hinata/logs/, Telegram only on terminal failure LogsPi keeps only "fired at T" lines (lightweight)Z4 keeps full stdout/stderr per run
Trigger transport — SSH vs webhook
A. SSH-based (simpler, no extra services)
*/30 * * * * ssh -o BatchMode=yes z4 '/opt/hinata/bin/run-job poll-monzo'Requires key-only auth Pi → Z4, no passphrase. Trade-off: SSH overhead per fire (~100-200ms); fine at 30-min cadence, awkward at 1-min cadence.
B. Webhook-based (cleaner, scales to sub-minute)
*/30 * * * * curl -X POST http://z4.lan:8421/jobs/poll-monzoZ4 runs a small HTTP listener on local LAN: POST /jobs/{name} triggers a systemd one-shot. Trade-off: extra service to keep alive on Z4 (hinata-jobs.service); needs LAN-only firewall rule.
Recommendation: start with A, migrate to B when sub-5-min cadence becomes routine (likely once #840052 hybrid live-update is in play).
Migration path (LaunchAgent → Pi cron)
Current state (2026-05-26): all scheduled work lives in ~/Library/LaunchAgents/com.hinata.*.plist on Michael's Mac. Mac sleeps overnight + isn't a server — that's the bug this entire migration fixes.
StepOwnerTriggerNotes
- Hardware arrivesMichael#840029 closureZ4 Mini + Pi delivered, racked, network-addressable
- Pi cron baselinejimmy-neutronhardware step 0 doneInstall cron, create
/opt/hinata/cron/with symlinked job scripts; pi-health heartbeat live - Z4 job runnerjimmy-neutronstep 1 done
hinata-jobs.service(or just/opt/hinata/bin/run-jobfor SSH path); per-job systemd one-shots staged - Migrate jobs one at a timetrunksstep 2 donePer job: write Z4-side script, add Pi cron entry, disable matching LaunchAgent, verify 24h, then delete LaunchAgent plist
- Final LaunchAgent retirementjimmy-neutronall jobs migrated
~/Library/LaunchAgents/com.hinata.*.plistdeleted; Mac is no longer in the scheduling path | | | | | | --- | --- | --- | --- | | | | | | | | | | | | | | | | | | | | | | | | | |
What stays on the Mac
Apple Health export hooks —
health-exportLaunchAgent must run on the device that owns HealthKit access. Keep on Mac, push CSVs to Z4 via rsync/scp.iCloud-bound jobs — anything that reads from the vault directly. Vault is iCloud; Mac is the synced device.
Interactive .command launchers — Finder double-click UX requires GUI session. Keep on Mac.
Everything else moves.
Open follow-ups
* **#840029 (NUC migration)** — gating hardware item; nothing in this doc unblocks until it lands.
* **#840041 (jimmy-vps inbox endpoint)** — webhook receiver for option B; build alongside step 2.
* **#840051 (self-hosted LLM)** — Ollama on Z4 is a separate workload; cron triggers it like any other job.
* **#840052 (website live-update)** — sub-5-min cadence makes webhook (option B) the right transport from day 1.
Closure trigger
LaunchAgent → systemd-timer (or systemd-service + Pi cron) migration done with Pi as the cron host. ~/Library/LaunchAgents/com.hinata.*.plist count drops to ≤3 (the three Mac-bound exceptions above). #840053 closes when migration step 4 completes.
◆ hinata · projects/hinata-infrastructure/cron-orchestration.html · phase-19 conversion