Metadata-Version: 2.4
Name: runspec-scheduler
Version: 0.1.0
Summary: Cron-style scheduler service for runspec runnables installed in one venv
License-Expression: MIT
Requires-Python: >=3.10
Requires-Dist: apscheduler<4,>=3.10
Requires-Dist: runspec>=0.48.0
Requires-Dist: tomli>=2.0; python_version < '3.11'
Provides-Extra: dev
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff==0.15.20; extra == 'dev'
Description-Content-Type: text/markdown

# runspec-scheduler

**Cron, but limited to runspec runnables installed in one venv.**

A long-running per-venv scheduler daemon (`systemctl --user`) that runs runnables
installed in *its own venv* on a cron/interval cadence — independent of any desktop
console. It is itself a runspec runnable, so `runspec-console` can detect it and
manage its schedules over SSH, but `discoverable = false` keeps it out of the Forms
tab / `runspec serve` MCP surface.

## Why

A console-driven schedule only fires while the desktop console is open, and it's
per-operator. `runspec-scheduler` gives you **shared, always-on** schedules that live
on the host and run whether or not a console is connected.

## Install

```bash
# Into the same venv whose runnables you want to schedule:
pip install runspec-scheduler

# Install + enable the systemd --user service for this venv:
runspec-scheduler install-service
loginctl enable-linger "$USER"     # survive logout
```

`install-service` writes `~/.config/systemd/user/runspec-scheduler-<venv>.service`
(one daemon per venv) and runs `systemctl --user enable --now`. To do it by hand, see
`deploy/runspec-scheduler.service`.

## Use

```bash
runspec-scheduler add --id nightly --runnable backup --cron '0 2 * * *'
runspec-scheduler add --id poll   --runnable healthcheck --every 15m --args '{"level":"warn"}'
runspec-scheduler list
runspec-scheduler run-now --id poll
runspec-scheduler remove --id poll
```

- Exactly one of `--cron` (5-field cron) or `--every` (`15m` / `1h` / `1d`).
- `--args` is a JSON object passed to the target runnable.
- Adding/removing a schedule hot-reloads the running daemon (mtime poll, ≤ 5s).

## Config

Schedules live in `{venv}/runspec_scheduler.toml` (override with `--config` or
`$RUNSPEC_SCHEDULER_CONFIG`). The daemon keeps a rotating audit log at
`{venv}/logs/scheduler.log`; live logs via `journalctl --user -u runspec-scheduler-<venv> -f`.

## Credentials (`run_as` runnables)

`runspec-console` can attach service-account credentials to a remote schedule. For a
runnable that declares `run_as`, the selected credentials are written to a **600 env
file owned by the `run_as` account** in that account's home; the daemon points
`RUNSPEC_ENV_FILE` at it and escalates to `run_as` when firing. Credentials are never
placed on a command line. This requires the daemon/SSH user to have passwordless
`sudo -u <run_as>` (the same precondition as running that `run_as` runnable at all).

Runnables **without** `run_as` run as the daemon's own user with the daemon's
environment (systemd `EnvironmentFile`) — no console credential injection.
