npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cronology

v0.1.0

Published

Ephemeral task watcher service — zero-cost polling daemon with webhook callbacks

Readme

   ██████╗██████╗  ██████╗ ███╗   ██╗ ██████╗ ██╗      ██████╗  ██████╗ ██╗   ██╗
  ██╔════╝██╔══██╗██╔═══██╗████╗  ██║██╔═══██╗██║     ██╔═══██╗██╔════╝ ╚██╗ ██╔╝
  ██║     ██████╔╝██║   ██║██╔██╗ ██║██║   ██║██║     ██║   ██║██║  ███╗ ╚████╔╝
  ██║     ██╔══██╗██║   ██║██║╚██╗██║██║   ██║██║     ██║   ██║██║   ██║  ╚██╔╝
  ╚██████╗██║  ██║╚██████╔╝██║ ╚████║╚██████╔╝███████╗╚██████╔╝╚██████╔╝   ██║
   ╚═════╝╚═╝  ╚═╝ ╚═════╝ ╚═╝  ╚═══╝ ╚═════╝ ╚══════╝ ╚═════╝  ╚═════╝    ╚═╝

Ephemeral task watcher service — zero-cost polling daemon with webhook callbacks

npm version license node CI


What is Cronology?

Cronology is a lightweight, self-hosted polling daemon that watches ephemeral tasks and notifies you when they complete. Unlike monitoring tools that run 24/7, cronology watchers are born to die — they poll until a condition is met, fire a webhook, and disappear.

Use cases:

  • 🚀 Watch a deploy until it's healthy, then notify Slack
  • 📦 Wait for a build artifact to appear on S3
  • 🔄 Poll a CI pipeline until it finishes
  • 📁 Watch for a file to appear (or disappear)
  • ⏰ Any "poll until done" workflow with automatic cleanup

Features

| Feature | Description | |---------|-------------| | 🪶 Lightweight | SQLite storage, ~14KB scheduler. No Redis, no Postgres | | ⏳ Ephemeral | Watchers auto-clean after completion or timeout | | 🔌 Templates | Built-in HTTP & file templates. Write custom ones in TypeScript | | 📡 Webhooks | POST results on completion with Bearer auth support | | 🖥️ Dashboard | React web UI for monitoring and management | | 🐳 Docker | Single-container deployment with volume persistence | | ⌨️ CLI | Full CLI for scripting and automation | | 🔧 systemd | Built-in unit file generator for Linux servers |

Quick Start

npm

npm install -g cronology
cronology serve
# Open http://localhost:18790

Docker

docker run -d \
  -p 18790:18790 \
  -v cronology-data:/app/data \
  --name cronology \
  exisz/cronology

Docker Compose

services:
  cronology:
    build: .
    container_name: cronology
    restart: unless-stopped
    ports:
      - "18790:18790"
    volumes:
      - ./data:/app/data
    environment:
      - CRONOLOGY_DATA_DIR=/app/data

CLI Reference

# Start the daemon
cronology serve [--port 18790] [--host 0.0.0.0]

# Register a watcher
cronology add <template> <name> [options]
  --payload '{...}'          # JSON payload for the template
  --interval 30s             # Poll interval (30s, 3m, 1h)
  --timeout 2h               # Max watcher lifetime
  --callback-url <url>       # Webhook URL on completion
  --callback-token <token>   # Bearer token for webhook

# Manage watchers
cronology list               # List active watchers
cronology status <name>      # Watcher details
cronology remove <name>      # Remove a watcher

# History & info
cronology history            # Show completed watchers
cronology templates          # List available templates
cronology health             # Daemon health check

# System
cronology install-service    # Generate systemd unit file

Examples

# Watch a deploy until healthy
cronology add http-status deploy-check \
  --payload '{"url":"https://app.example.com/health"}' \
  --interval 30s --timeout 15m \
  --callback-url https://hooks.slack.com/services/...

# Watch for a build artifact
cronology add file-watch build-output \
  --payload '{"path":"/tmp/build/output.tar.gz"}' \
  --interval 10s --timeout 30m

# Watch until a file is removed
cronology add file-watch cleanup-check \
  --payload '{"path":"/tmp/lockfile","condition":"not-exists"}' \
  --interval 5s --timeout 10m

API Reference

All endpoints are under http://localhost:18790/api/.

| Method | Endpoint | Description | |--------|----------|-------------| | GET | /api/health | Health check with watcher count | | GET | /api/templates | List available templates | | GET | /api/templates/:name | Template details | | POST | /api/watchers | Register a new watcher | | GET | /api/watchers | List active watchers | | GET | /api/watchers/:name | Watcher details | | DELETE | /api/watchers/:name | Remove a watcher | | GET | /api/history | Completed/timed-out watchers | | DELETE | /api/history | Clear history |

Register a Watcher (POST /api/watchers)

{
  "template": "http-status",
  "name": "my-deploy-check",
  "payload": {
    "url": "https://app.example.com/health",
    "expectedStatus": 200
  },
  "intervalMs": 30000,
  "timeoutMs": 900000,
  "callbackUrl": "https://hooks.example.com/webhook",
  "callbackToken": "secret-token"
}

Webhook Callback Payload

When a watcher completes (or times out), cronology POSTs:

{
  "event": "watcher.completed",
  "watcher": "my-deploy-check",
  "template": "http-status",
  "status": "done",
  "result": { "status": 200, "statusMatch": true, "bodyMatch": true },
  "pollCount": 5,
  "createdAt": "2025-01-15T10:00:00.000Z",
  "completedAt": "2025-01-15T10:02:30.000Z",
  "durationMs": 150000
}

Built-in Templates

http-status

Poll an HTTP endpoint until it returns the expected status/body.

| Payload Field | Type | Default | Description | |---------------|------|---------|-------------| | url | string | required | URL to poll | | method | string | GET | HTTP method | | expectedStatus | number | 200 | Expected status code | | expectedBody | string | — | String that must appear in body | | headers | object | — | Additional headers | | timeout | number | 10000 | Request timeout (ms) |

file-watch

Watch for file existence, absence, or modification.

| Payload Field | Type | Default | Description | |---------------|------|---------|-------------| | path | string | required | File path to watch | | condition | string | exists | exists, not-exists, or modified-after | | after | string | — | ISO timestamp for modified-after |

Writing Custom Templates

Create a TypeScript file implementing the WatcherTemplate interface:

import { WatcherTemplate, PollContext, PollResult } from "../templates.ts";

export const myTemplate: WatcherTemplate = {
  name: "my-check",
  description: "Description of what this checks",
  payloadSchema: { /* JSON Schema */ },
  defaults: { intervalMs: 30_000, timeoutMs: 3_600_000 },

  async poll(payload: unknown, ctx: PollContext): Promise<PollResult> {
    // Your polling logic here
    return {
      done: false,       // true when condition is met
      data: { ... },     // arbitrary data (included in webhook)
      message: "...",    // log message
    };
  },

  format(payload: unknown, result: unknown): string {
    return "Human-readable summary";
  },
};

Register it in src/core/templates/index.ts:

import { myTemplate } from "./my-check.ts";
registerTemplate(myTemplate);

Comparison

| Feature | Cronology | healthchecks.io | Gatus | changedetection.io | BullMQ | |---------|-----------|-----------------|-------|-------------------|--------| | Purpose | Ephemeral task watching | Cron monitoring | Uptime monitoring | Web change detection | Job queue | | Ephemeral watchers | ✅ Core feature | ❌ | ❌ | ❌ | ~ish | | Self-hosted | ✅ Single process | ✅ Complex | ✅ | ✅ | Needs Redis | | External deps | None (SQLite) | PostgreSQL | None | — | Redis | | Webhook on complete | ✅ | ✅ | ✅ | ✅ | Via code | | Web GUI | ✅ Built-in | ✅ | ✅ | ✅ | Bull Board | | CLI | ✅ | ❌ | ❌ | ❌ | ❌ | | Template system | ✅ | ❌ | YAML | ❌ | ❌ |

Configuration

| Environment Variable | Default | Description | |---------------------|---------|-------------| | PORT | 18790 | HTTP server port | | HOST | 0.0.0.0 | Bind address | | CRONOLOGY_DATA_DIR | ./data | SQLite data directory |

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT © exisz