Chronos API
The centralized agent scheduler: agent DID authentication plus create, list, get, and cancel alarms that wake an agent at a scheduled time.
Chronos (chronosd) is the centralized agent scheduler and wake-up system. Agents authenticate with their DID, then create alarms that fire a wake at a scheduled time. Chronos uses two-layer auth: a shared transport bearer (CHRONOS_TOKEN) proves the caller is a legitimate Matrix daemon, and an ed25519 agent-DID principal token (X-Chronos-Agent) proves which owner is calling.
Routes
| Method | Path | Purpose |
|---|---|---|
| GET | /healthz | Unauthenticated liveness (pings database) |
| GET | / | Service root: name, version, health link |
| POST | /v1/agent/auth/challenge | Request a nonce for the agent DID |
| POST | /v1/agent/auth/verify | Verify the signed nonce, mint a session token |
| POST | /v1/alarms | Create an alarm |
| GET | /v1/alarms | List the agent's alarms |
| GET | /v1/alarms/{id} | Get one alarm |
| DELETE | /v1/alarms/{id} | Cancel an alarm |
Authentication
Two-layer auth on all /v1/* paths:
- Transport bearer --
Authorization: Bearer <CHRONOS_TOKEN>proves the caller is a Matrix daemon. - Principal token --
X-Chronos-Agent: <token>proves which agent/owner. Minted via the challenge/verify handshake below.
See the shared agent DID handshake.
Challenge
POST /v1/agent/auth/challenge
didstringrequiredThe agent DID (did:matrix:<owner_user_id>:<key_fingerprint>).
Returns a nonce and the exact message to sign. The nonce is single-use and expires after the configured TTL.
Verify
POST /v1/agent/auth/verify
didstringrequiredThe agent DID.
public_keystringrequiredHex-encoded ed25519 public key (must match the DID fingerprint).
noncestringrequiredThe nonce from the challenge response.
signaturestringrequiredHex-encoded ed25519 signature over matrix-chronos-auth:<did>:<nonce>.
Returns a short-lived HMAC principal token and the owner user ID.
Create an alarm
POST /v1/alarms requires the X-Chronos-Agent principal token.
kindstringrequiredonce or cron.
wake_messagestringrequiredThe contextful turn delivered to the agent on wake.
delay_secondsintegerFor once: seconds from now until fire.
fire_atstring (ISO 8601)For once: absolute fire time.
cron_exprstringFor cron: a standard cron expression (5-field).
timezonestringFor cron: IANA timezone (default UTC).
labelstringHuman-readable label for the alarm.
conversation_idstringConversation to resume on wake.
payloadobjectOpaque data delivered to the agent on wake.
idempotency_keystringDeduplicates retries; same key + owner returns the existing alarm.
max_failuresintegerMax consecutive failures before terminal fail (default 5).
curl -X POST https://chronos.example/v1/alarms \
-H "Authorization: Bearer $CHRONOS_TOKEN" \
-H "X-Chronos-Agent: $PRINCIPAL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"kind": "once",
"delay_seconds": 3600,
"wake_message": "Time for your daily digest.",
"label": "daily-digest"
}'idstringrequiredThe alarm identifier (used for get/cancel).
next_fire_atstringISO 8601 timestamp of the next scheduled fire.
statusstringactive, fired, cancelled, or failed.
List alarms
GET /v1/alarms?limit=100 returns the caller's alarms, most-recent first. The limit query parameter defaults to 100 (max 500).
Get an alarm
GET /v1/alarms/{id} returns a single alarm by ID, owner-checked.
Cancel an alarm
DELETE /v1/alarms/{id} marks an active alarm as cancelled. Already-fired or already-cancelled alarms return success without modification.
Alarm lifecycle
| Status | Meaning |
|---|---|
active | Scheduled and waiting to fire |
fired | Successfully delivered (once alarms are terminal) |
cancelled | Manually cancelled by the owner |
failed | Retries exhausted (once alarms are terminal; cron alarms skip and advance) |
Cron alarms automatically reschedule after each successful fire. Failed cron fires use a bounded backoff ladder: retry, then skip-and-advance so one bad fire does not wedge the series.
All alarm operations are idempotent on the idempotency_key. Concurrent dispatchers use FOR UPDATE SKIP LOCKED to avoid double-claiming.
