Matrix logo

Chronos Developer Documentation

Chronos is Matrix's centralized agent scheduler and wake control plane. One always-on chronosd service serves the entire fleet: agents POST alarms (once or cron) with a contextf...

Chronos is Matrix's centralized agent scheduler and wake control plane. One always-on chronosd service serves the entire fleet: agents POST alarms (once or cron) with a contextful wake payload, Chronos durably stores them in Postgres, fires due alarms, and asks the router to wake the agent and deliver the resume turn.

This documentation is written for people working on Chronos itself — extending the schedule engine, modifying the dispatch worker, wiring new tools, or understanding how the wake path works.


Contents

DocumentWhat it covers
ArchitectureThe 5-layer design, topology, network directions, and mission
Data ModelThe alarms table, Alarm struct, View projection, lifecycle statuses
Schedule Engineonce and cron kinds — NextOnce, NextCron, timezone handling, robfig/cron/v3
Dispatch WorkerPoll-and-claim loop, FOR UPDATE SKIP LOCKED, retry ladder, at-least-once delivery
Wake DeliveryThe 6-step wake path: chronosd → router → Fly → daemon → Neo
Auth SystemTwo-layer auth: transport bearer + agent-DID ed25519 challenge/verify + HMAC principal tokens
API ReferenceHTTP surface — healthz, agent auth, alarm CRUD, request/response shapes
Config SystemEnv-first configuration with optional chronos.config.kvx overlay
Tool SurfaceMCP stdio proxy — alarm_set, alarm_list, alarm_cancel, selftest, manifest bijection

Repository layout

chronos/
├── cmd/chronosd/
│   └── main.go              # Service entry: config load, store init, worker spawn, HTTP server
├── internal/
│   ├── auth/
│   │   ├── identity.go      # DID parsing, ed25519 challenge/verify, single-use nonce store
│   │   ├── token.go         # Stateless HMAC principal tokens (mint + verify)
│   │   └── auth_test.go     # Round-trip tests for DID, challenge, signature, token
│   ├── config/
│   │   ├── config.go        # Config struct, Load(), env-over-kvx-over-defaults
│   │   └── kvx.go           # Zero-dep .kvx parser (sectioned key/value, ${ENV} interpolation)
│   ├── dispatch/
│   │   └── dispatch.go      # Poll-and-claim worker: ClaimDue, fire, retry ladder, backoff
│   ├── schedule/
│   │   ├── schedule.go      # NextOnce (delay/absolute), NextCron (5-field/@descriptor/@every)
│   │   └── schedule_test.go # Unit tests for all schedule paths
│   ├── server/
│   │   └── server.go        # HTTP mux: healthz, agent auth, alarm CRUD, transport middleware
│   ├── store/
│   │   ├── store.go         # Postgres pool, migration runner (chronos_schema_migrations)
│   │   └── alarms.go        # Alarm queries: create (idempotent), list, get, cancel, claim-due, reschedule, retry, fail
│   ├── telemetry/
│   │   └── telemetry.go     # Structured JSON logger (slog)
│   └── wake/
│       └── wake.go          # HTTPWaker — POSTs /internal/wake to the router with wake token
├── migrations/
│   └── 001_init.sql         # alarms table + indexes (due, owner, idempotency)
├── pkg/types/
│   └── types.go             # Wire contracts: Envelope, Error, Alarm, View, CreateAlarmRequest, auth types
├── chronos.frozen.kvx       # Frozen architecture spec (design contract)
├── go.mod
└── go.sum

The one-sentence contract

Chronos takes an alarm (a future time + a contextful wake message + an opaque payload), durably stores it in Postgres, fires it when due, and delivers the wake to the agent via the router's proven machine-wake path. The agent resumes into its stored conversation with enough context to pick up exactly where it left off.

That is the whole point. It's the boundary between the agent's episodic, scale-to-zero world and the durable, always-on world of time.


Key locked decisions

These decisions are frozen in chronos.frozen.kvx. Don't re-litigate them without an explicit spec version bump.

IDDecision
i1Alarms are durable — a Chronos restart never loses a scheduled wake (state lives in Postgres, not memory)
i2DID-scoped — an agent can only create/list/cancel its own alarms; the wake target is derived from the verified DID, never caller-supplied
i3At-least-once delivery with idempotency — a fire is recorded only after the router confirms; duplicates are dedup-able
i4Context fidelity — wake_message + payload are delivered verbatim; high-entropy tokens (ids, hashes, cursors) are never paraphrased
i5Off-chain only — Chronos holds no signing key and performs no on-chain action; deferred money escalates to MCL at fire time
i6Honest failure — an undeliverable wake is retried then surfaced (status + last_error); never fabricated
i7Chronos never writes cortex / signs envelopes / touches plan-walk — it delivers a chat turn via the existing /chat path; the agent does the work
i8Tool registry ↔ manifest stay in strict bijection (chronos-tools.json == agents/*.json) or daemon boot is fatal