# koku

A system-level cron daemon written in Rust. Japanese for "to mark/carve time" (刻).

Single binary that manages and runs scheduled jobs, with an integration point for kagaya to surface cron status and trigger one-off runs. CLI alias: `ku`.

## What it is

- A single binary (`koku`) — `ku daemon start` runs the daemon, all other subcommands talk to it via Unix socket
- CLI for managing jobs: status, one-off runs, pause/resume, log viewing
- A library crate others can embed if needed

## Architecture

- **Scheduler**: `croner` for cron expression parsing, custom scheduler loop on top of tokio
- **Daemon**: `ku daemon start` runs the scheduler in background (`ku daemon run` for foreground). Unix socket IPC (via muzan) for CLI ↔ daemon communication
- **Config**: TOML file defining jobs — name, cron expression, command, working dir, error policy
- **Logging**: Per-job structured log output (job name, exit code in every line)
- **Process execution**: Spawn jobs as child processes, capture stdout/stderr, track exit codes

## Config format

```toml
[[job]]
name = "backup"
cron = "0 2 * * *"
command = "bash backup.sh"
dir = "/home/user/scripts"
on_error = "backoff"   # continue | backoff | stop (default: continue)
overlap = false        # allow concurrent runs (default: false)
timeout = "30m"        # kill job after duration (optional)

[[job]]
name = "cleanup"
cron = "*/15 * * * *"
command = "python3 cleanup.py"
```

### on_error policy (from Jobber's model)

- `continue` — ignore failures, keep scheduling (default)
- `backoff` — exponential backoff on failure, resume normal schedule on success
- `stop` — disable the job after repeated failures. Requires manual `ku resume` to re-enable

## Job states

```
idle      waiting for next scheduled run
running   currently executing
paused    manually paused via `koku pause`
failing   last run errored, will retry with backoff (on_error = backoff)
stopped   disabled after repeated failures (on_error = stop). manual resume required
```

## CLI

```
ku init              # scaffold ~/.config/koku/config.toml with examples
ku check             # validate config, preview next-run times
ku daemon start      # start daemon in background
ku daemon stop       # stop daemon gracefully
ku daemon status     # show daemon pid/uptime
ku status            # table of all jobs with state/timing
ku status --json     # machine-readable
ku run <name>        # trigger one-off run
ku pause <name>      # pause a job without editing config
ku resume <name>     # resume a paused/stopped job
ku reload            # reload config without restarting
```

### Status output

```
NAME       STATE    LAST RUN              EXIT  NEXT RUN
backup     idle     2026-02-18 02:00:00   0     2026-02-19 02:00:00
cleanup    running  2026-02-18 14:15:00   —     —
sync       failing  2026-02-18 14:00:00   1     2026-02-18 14:30:00
deploy     paused   2026-02-17 18:00:00   0     —
```

## Kagaya integration

kagaya connects to koku's Unix socket to:
- Display cron job status in the process supervisor UI
- Trigger one-off runs from within kagaya

## Crates

- `muzan` — daemon lifecycle (Unix socket IPC, PID management, auto-start). Path dep: `~/dev/muzan`
- `tokio` — async runtime
- `croner` — cron expression parsing (lightweight, no scheduler opinions)
- `serde` + `toml` — config parsing
- `clap` — CLI
- `tracing` — structured logging (with job name, exit code fields)
- `comfy-table` — status table output

## Design influences

- **Jobber** (Go, unmaintained) — error handling model (continue/backoff/stop), error vs failure distinction, `test`/`pause`/`resume` commands, `init` scaffolding
- **Supercronic** (Go) — structured log fields per job (iteration, schedule, command), overlap detection with warnings, `-test` flag for config validation
- **Cronitor CLI** (Go) — `--json` flag on every command, `pause --hours` timed pause, progressive disclosure in output
- **Healthchecks.io** (Python) — clean state machine (idle/running/late/down), grace period concept
- **Cronie** (C) — `cronnext` for previewing schedules (→ `koku check`)

No Rust CLI cron manager exists. koku fills that gap.

## Future (post-v1)

- `ku log <name>` — per-job log viewing (requires per-job log files)
- Notification hooks (`on_failure` command per job, webhook support)
- Timed pause (`ku pause <name> --hours 24`)
- Random schedule jitter (Jobber's `R`, Cronie's `~`)
- Env vars per job in config

Web dashboard is provided by kagaya (`ky serve`), which connects to koku's Unix socket for cron status, one-off runs, and pause/resume.

## Naming

- Crate: `koku`
- Binaries: `koku` + `ku` (alias)
- Daemon: `ku daemon start` / `ku daemon run`
- Named after 刻 (koku) — Japanese for "to carve/mark time"
