Metadata-Version: 2.4
Name: deadcron
Version: 0.1.0
Summary: Local dead-man's-switch for cron jobs — get alerted when a scheduled job silently stops running. Zero dependencies, no server.
Author: yyfjj
License: MIT
Project-URL: Homepage, https://pypi.org/project/deadcron/
Keywords: cron,monitoring,heartbeat,dead-mans-switch,cli,scheduler,alert,devops,selfhosted
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# deadcron (Python)

**A local dead-man's-switch for cron jobs.** Cron never tells you when a job
*stops* running — the backup that silently hasn't fired in three days, the sync
that's been crashing since Tuesday. `deadcron` notices the silence and alerts
you. **Zero dependencies** (pure stdlib), **no server to host**.

> There's also an identical [npm version](https://www.npmjs.com/package/deadcron)
> (`npx deadcron`). Both share the same `~/.deadcron` state format.

```bash
pip install deadcron

# Replace your crontab command with a wrapped version:
*/30 * * * *  deadcron run backup --every 1h -- /usr/local/bin/backup.sh

# Then let the watcher check for silence:
deadcron install --every 5m
```

## Why

> "Cron jobs do not tell you when they fail. That is the core issue."

The classic incident: a database backup script dies at 2 a.m., keeps "running"
in cron's eyes, and nobody finds out until the day you actually need the backup.
Hosted monitors (healthchecks.io, Cronitor, Dead Man's Snitch) solve this — but
they need an account and send your job metadata to a third party. `deadcron`
runs entirely on your machine: a small state file plus a watcher you schedule
yourself.

## How it works

1. **Each run checks in** — wrap the command with `run` (pings on exit 0,
   records the exit code on failure) or call `ping <name>` at the end of your job.
2. **A job declares its expected period** with `--every` (+ optional `--grace`).
   If the time since the last successful check-in exceeds `every + grace`, the
   job is **overdue**.
3. **The watcher runs `check`** on its own schedule (added to cron once via
   `install`) and fires every alert channel you've enabled.

## Tracking a job

```bash
# Recommended: wrap the command.
deadcron run backup --every 1d --grace 1h -- /opt/backup.sh

# Or ping manually at the end of a script:
deadcron register backup --every 1d --grace 1h
deadcron ping backup

deadcron status
#  ● backup    ok                every 1d   last: 3h ago
#  ● sync      OVERDUE by 2h     every 1h   last: 3h ago
```

## The watcher

```bash
deadcron check                  # exit 1 if anything is overdue/failed
deadcron check --json
deadcron install --every 5m     # add `check` to crontab (idempotent)
deadcron uninstall
```

`check` throttles repeat alerts (default: at most once per hour per job).

## Alert channels

`check` fires **every enabled channel** at once. Terminal is on by default.

```bash
deadcron config show
deadcron config enable macos
deadcron config set-webhook https://hooks.slack.com/services/XXX
deadcron config set-email --to ops@you.com --sendmail
deadcron config set-email --to ops@you.com --from cron@you.com \
    --smtp-host smtp.gmail.com --smtp-port 465 \
    --smtp-user me@gmail.com --smtp-pass "app-password"
deadcron config test            # send a sample alert through all of them
```

| Channel | How |
|---------|-----|
| **terminal** | writes the alert to stderr |
| **macOS** | native banner via `osascript` |
| **webhook** | `POST` JSON `{event, checkedAt, jobs}` |
| **email** | local `sendmail`, or direct SMTP (`smtplib`, TLS 465 or STARTTLS) |

## Managing jobs

```bash
deadcron pause <name>     # maintenance window
deadcron resume <name>
deadcron fail <name>      # mark last run as failed
deadcron rm <name>
```

## Storage

Plain JSON under `~/.deadcron/` (override with `$DEADCRON_HOME`). Nothing leaves
your machine unless you configure a webhook or email channel.

## Exit codes

`0` healthy · `1` one or more jobs overdue/failed · `2` error.

## Duration format

`30s` · `5m` · `2h` · `1d` · `1w` (a bare number means seconds).

## License

MIT
