Skip to content

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

  1. 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.

  2. 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.

  3. 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-monzo

Z4 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

  1. Hardware arrivesMichael#840029 closureZ4 Mini + Pi delivered, racked, network-addressable
  2. Pi cron baselinejimmy-neutronhardware step 0 doneInstall cron, create /opt/hinata/cron/ with symlinked job scripts; pi-health heartbeat live
  3. Z4 job runnerjimmy-neutronstep 1 donehinata-jobs.service (or just /opt/hinata/bin/run-job for SSH path); per-job systemd one-shots staged
  4. 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
  5. Final LaunchAgent retirementjimmy-neutronall jobs migrated~/Library/LaunchAgents/com.hinata.*.plist deleted; Mac is no longer in the scheduling path | | | | | | --- | --- | --- | --- | | | | | | | | | | | | | | | | | | | | | | | | | |

What stays on the Mac

  • Apple Health export hookshealth-export LaunchAgent 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