Qhronos: Reliable Scheduling & Webhook Delivery

Table of Contents
API Documentation
See docs/api.md for full API details and example requests/responses.
Overview
Qhronos (v0.1.0) is a developer-first scheduling and notification platform. It lets you schedule one-time or recurring events and reliably delivers webhooks at the right time. Built for reliability, security, and extensibility, Qhronos is ideal for automating workflows, orchestrating AI agents, and managing time-based triggers in distributed systems.
Event → Schedule → Occurrence Flow:
- Event: User-defined intent and configuration, stored in the database.
- Schedule: Actionable plan (stored in Redis) for when the event should be executed. Created by the expander.
- Occurrence: A record representing a specific scheduled instance of an Event. For recurring events, these are generated by the Expander. Each Occurrence tracks its lifecycle (e.g., pending, scheduled, dispatched, failed) and the outcome of its execution attempt by the Dispatcher.
For system architecture and in-depth design, see design.md.
Features
- REST API for event scheduling
- Recurring and one-time events (iCalendar RFC 5545)
- Reliable, retryable webhook delivery
- JWT and master token authentication
- Rate limiting and audit logging
- Easy deployment (Docker/Kubernetes)
Quick Start
Prerequisites
- Go 1.20+
- Docker & Docker Compose
- PostgreSQL & Redis (or use Docker Compose)
First-Time Setup
For your first run, copy the example config, build the Docker image, and start only the database dependencies and run migrations:
cp config.example.yml config.yml
make docker-build
make docker-up
make migrate-up
Then, to start all services (including the app):
make docker-qup
The API will be available at http://localhost:8080
.
Build and Run from Binary
make build
./bin/qhronosd --config config.yaml
You can use CLI flags to override config values, e.g.:
./bin/qhronosd --port 9090 --log-level debug
Configuration Setup
Before running Qhronos, set up your configuration:
- Copy the example config file:
cp config.example.yaml config.yaml
- Edit
config.yaml
to match your environment (database, Redis, auth secrets, etc).
- You can also override any config value using CLI flags (see above).
Database Setup & Migration
Before running Qhronos for the first time, initialize the PostgreSQL database and apply migrations:
- Start PostgreSQL and Redis using Make:
make docker-up
- Run database migrations:
make migrate-up
- Start all services (including Qhronos):
make docker-qup
This will create all required tables and schema in your database.
Database Migration
Qhronos manages database schema changes using embedded migration files. You can apply all migrations using:
./bin/qhronosd --migrate --config config.yaml
- This will apply all pending migrations to your database.
- You can use any CLI flags to override config values (e.g., DB host, port, user).
Notes
- Migration files are embedded in the binary; no external migration tool is needed.
- The binary manages a
schema_migrations
table to track applied migrations.
- Migration files should be named with incremental prefixes (e.g.,
001_initial_schema.sql
, 002_add_table.sql
, etc.).
API Usage
See API documentation for full details.
Event Creation Example
You can now specify an action for event delivery. The action
field supports both webhook and websocket types.
Webhook Example
{
"name": "My Event",
"description": "A test event",
"start_time": "2025-01-01T00:00:00Z",
"action": {
"type": "webhook",
"params": { "url": "https://example.com/webhook" }
},
"tags": ["api"]
}
Websocket Example
{
"name": "Websocket Event",
"description": "A test event for websocket client",
"start_time": "2025-01-01T00:00:00Z",
"action": {
"type": "websocket",
"params": { "client_name": "client1" }
},
"tags": ["api"]
}
API Call Example
{
"name": "API Call Event",
"description": "A test event for apicall action",
"start_time": "2025-01-01T00:00:00Z",
"action": {
"type": "apicall",
"params": {
"method": "POST",
"url": "https://api.example.com/endpoint",
"headers": { "Authorization": "Bearer token", "Content-Type": "application/json" },
"body": "{ \"foo\": \"bar\" }"
}
},
"tags": ["api"]
}
Backward Compatibility
The legacy webhook
field is still supported for backward compatibility. If you provide webhook
, it will be automatically mapped to the appropriate action
.
Action System
Qhronos uses an extensible action system for event delivery. Each event can specify an action
object with a type
and params
. Supported types:
webhook
: Delivers the event to an HTTP endpoint.
websocket
: Delivers the event to a connected websocket client.
apicall
: Makes a generic HTTP request with custom method, headers, body, and url.
The system is designed to be extensible for future action types.
Schedule Parameter Tutorial
The schedule
parameter in event creation allows you to define recurring or one-time schedules using a flexible JSON structure. Here are the most common use cases:
1. One-Time Event (No Recurrence)
If you omit the schedule
field, the event will be scheduled only once at the specified start_time
:
{
"name": "One-Time Event",
"description": "This event happens only once.",
"start_time": "2024-05-01T10:00:00Z",
"webhook": "https://example.com/webhook",
"metadata": {},
"tags": ["single"]
// No "schedule" field!
}
2. Daily Recurring Event
"schedule": {
"frequency": "daily"
}
This schedules the event to occur every day. (If interval
is omitted, it defaults to 1.)
3. Weekly Recurring Event (e.g., every Monday and Friday)
"schedule": {
"frequency": "weekly",
"by_day": ["MO", "FR"]
}
This schedules the event to occur every Monday and Friday.
Tip: Omitting the schedule
field results in a one-time event. For recurring events, specify the schedule
field as shown above.
Configuration
- Copy
config.example.yaml
to config.yaml
and edit as needed.
- Or set CLI flags to override config values.
Scheduler Lookahead Settings
The scheduler section in your config controls how far into the future recurring events are expanded and how often the expander runs:
scheduler:
look_ahead_duration: 24h # How far into the future to expand recurring events
expansion_interval: 5m # How often to run the expander
look_ahead_duration
: Controls the window (e.g., 24h) for which recurring event occurrences are pre-generated.
expansion_interval
: How frequently the expander checks and generates new occurrences.
Adjust these values in your config.yaml
to tune scheduling behavior for your workload.
WebSocket Real-Time Event Delivery
Qhronos supports real-time event delivery via a WebSocket endpoint at /ws
.
Connection Types
- Client-Hook Listener: Receives events where the event webhook is
q:<client-name>
.
- Tag-Based Listener: Receives events that match any of the specified tags.
Usage
- Connect to the WebSocket endpoint:
ws://<host>/ws
- Send an initial handshake message:
- For client-hook:
{ "type": "client-hook", "client_name": "acme-corp", "token": "<JWT>" }
- For tag-listener:
{ "type": "tag-listener", "tags": ["billing", "urgent"], "token": "<JWT>" }
- On success, the server will send event messages as they occur:
{ "type": "event", "event_id": "evt_123", "occurrence_id": "occ_456", "payload": { ... }, "tags": ["foo", "bar"] }
- (Client-hook only) To acknowledge receipt:
{ "type": "ack", "event_id": "evt_123", "occurrence_id": "occ_456" }
- Tag-listener connections will receive an error if they send an ack message.
Security
- All connections require authentication via JWT or master token.
- Only authorized clients receive events for their hooks or allowed tags.
See the design document for more details on message flows and security.
Deployment
- Supports Docker, Docker Compose, and Kubernetes.
- See deployment guide for production tips.
Docker Usage
You can build and run Qhronos using Docker:
Build the Docker image
make docker-build
# or
# docker build -t qhronosd:latest .
Run all services with Make (Recommended)
make docker-qup
Stop all services
make docker-qdown
Override configuration
You can combine these methods as needed.
Authentication
- Use a master token or generate JWTs via the
/tokens
endpoint.
- See docs/auth.md for details.
JWT Tokens
Qhronos supports JWT (JSON Web Token) authentication for secure, scoped API access.
- Obtaining a JWT Token:
Use your master token to request a JWT via the
/tokens
endpoint:
curl -X POST http://localhost:8080/tokens \
-H "Authorization: Bearer <master_token>" \
-H "Content-Type: application/json" \
-d '{
"sub": "your-user-id",
"access": "admin",
"scope": ["user:your-username"],
"expires_at": "2024-12-31T23:59:59Z"
}'
Occurrences
An Occurrence represents a single execution attempt of an event, created only after a scheduled event (from Redis) is executed by the dispatcher. For recurring events, multiple occurrences are generated as each scheduled execution is processed. Each occurrence tracks its status, attempts, and delivery history.