Documentation Index
Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
Use this file to discover all available pages before exploring further.
The Flow Scheduler fires due pipelines from their ir.schedule. It
runs in-process by default via a 60-second setInterval registered
in instrumentation.ts on server startup, so kaireon ships complete
out of the box — no external cron service required. The same dispatch
logic is also exposed as the /api/v1/cron/flow-scheduler-tick HTTP
endpoint for self-hosters who prefer their own orchestrator (k8s
CronJob, EventBridge, Vercel Cron, etc.).
Multi-replica safety comes from a pg advisory lock — only one replica
or HTTP caller’s tick runs at a time per minute, the rest no-op with
{skipped: true}.
Configuration
| Env var | Default | Purpose |
|---|
CRON_SECRET | required | Shared secret for both the in-process ticker and the HTTP endpoint. The internal scheduler refuses to start if unset (logged warning) |
FLOW_INTERNAL_SCHEDULER_ENABLED | true | Set to "false" to disable in-process ticking — useful when you bring your own external scheduler |
FLOW_SCHEDULER_INTERVAL_MS | 60000 | Tick interval in milliseconds. Minimum 1000 |
FLOW_INTERNAL_SCHEDULER_BASE_URL | http://127.0.0.1:${PORT} | Where the in-process ticker fans out run-dispatch fetches |
| PORT | 3000 | Server port used to default the in-process scheduler base URL |
Manual tick from the editor
The Schedule tab inside the pipeline editor has a Tick scheduler now
button that hits the HTTP endpoint with the same-origin session cookie.
Useful when you’ve just saved a schedule and want to fire it without
waiting for the next minute boundary.
Endpoint
GET /api/v1/cron/flow-scheduler-tick
Authorization: Bearer $CRON_SECRET
(Or pass x-cron-secret: $CRON_SECRET header.)
Auth: Fails closed when CRON_SECRET env is unset (matches the
contract of /api/v1/cron/approvals-expire and other cron routes).
Response:
{
"tickAt": "2026-04-27T20:00:00.000Z",
"totalPipelines": 47,
"scheduled": 12,
"due": 3,
"dispatched": 3,
"failed": 0,
"results": [
{ "pipelineId": "...", "tenantId": "...", "fired": true, "status": 201 }
],
"durationMs": 412
}
Per-kind decision logic
Implemented as a pure due-pipelines helper in the platform’s flow
scheduling module — given the current set of schedules and the tick
time, it returns the IDs that fire on this tick.
| Kind | ”Due” condition |
|---|
| cron | cron-parser yields any occurrence in (lastRunAt, now] (most recent wins) |
| interval | now - lastRunAt >= minutes * 60_000 |
| rrule | RRule.between(lastRunAt, now) returns ≥ 1 occurrence |
lastRunAt = null (pipeline never ran): treated as 24h lookback to
avoid stampeding old, never-run pipelines that were created days/weeks
ago.
Bad schedules (malformed cron, malformed rrule) silently skip the
pipeline — parsePipelineIR would have rejected the IR save earlier
anyway.
How it fires
Default (in-process): The scheduler is self-firing. A
setInterval registered by Next.js’ instrumentation hook on server
boot calls the same dispatch path every 60 seconds via the platform’s
internal-ticker module. No external orchestrator required. App
Runner and any persistent-container deployment Just Work.
Optional (external): If you set
FLOW_INTERNAL_SCHEDULER_ENABLED=false (e.g. you’re on a serverless
runtime with no persistent process), wire any external scheduler to the
HTTP endpoint:
| Setup | Approach |
|---|
| Self-hosted (k8s) | A Kubernetes cron job resource hitting /api/v1/cron/flow-scheduler-tick every minute |
| Self-hosted (Linux box) | * * * * * curl -fsSL -H "x-cron-secret: $CRON_SECRET" https://your-app/api/v1/cron/flow-scheduler-tick |
| AWS | EventBridge Schedule → API destination |
| Vercel | A Vercel Cron Job (vercel.json crons array) hitting the endpoint |
Idempotency
- Calling the endpoint multiple times in one minute does not double-fire
a pipeline — once a run is dispatched,
lastRunAt advances to the
fire time, and the next tick’s (lastRunAt, now] window excludes the
same occurrence.
- Per-tick fanout is O(n) over all IR-native pipelines + 1 IR fetch
each. Phase 6.6 hardening adds an index + a “schedule_only” projection.
Honest limits
- Per-tenant scheduler dashboard showing miss / lag / failed
dispatches
trigger.file_arrival.deadline.{windowMinutes, onMiss} enforcement
— currently only pure schedule kinds fire; deadline windows on
file_arrival triggers are not yet checked
- Push-based file-arrival triggers (S3 ObjectCreated, SFTP fanotify,
inotify) are not yet implemented; today the source node’s
waitPolicy
provides pull-based file-arrival behavior