Kronical is a local activity-tracking service. The daemon (kronid) observes
system hooks, derives higher-level signals, and writes activity records that
clients can consume over HTTP/gRPC.
This repository focuses on the service side: hook ingestion, signal-driven state machine, compression, and storage backends. UI layers can build on the snapshot endpoints described below.
See docs/arch.md for the current pipeline topology, signal/hint derivation,
and storage hydration details.
crates/common/- shared utilities.crates/core/- domain logic and snapshot bus.crates/storage/- storage facade plus integration tests undercrates/storage/tests/.src/daemon/- runtime wiring: coordinator, pipeline, compressors, API surfaces, trackers.src/bin/- service binaries (kronid,kronictl).docs/- architecture notes and design documents.
Install it from source (kronictl, kronid):
cargo install --path .
kronictl start # launches the daemon, can optionally pass `--run <runId>`
kronictl status
kronictl restart # restarts the daemon with same runId
kronictl stop # stops the daemonAll configs, logs and databases are stored under ~/.kronical, you can simply delete that folder to reset the state.
kronid requires macOS Accessibility + Screen Recording permissions to capture
input events and window titles. Without them the daemon will exit early or
return empty focus data.
❯ kronictl status
Kronical Status Snapshot - 2026-01-03 20:14:40 -08:00
═════════════════════════════════════════════════════════════
Kronical Daemon:
State: Active
Run: PFgwShVsR5OM1HacwuD4jw
Focus: iTerm2 [2159] - jerry@Jerrys-MacBook-Pro:~
Cadence: 2000ms (Active)
Counts: signals=37833 hints=106 records=105
Next timeout: 2026-01-04 04:14:42.507038 UTC
Storage: backlog=3 last_flush=2026-01-04T04:14:36+00:00
Tip: Use 'kronictl monitor' for real-time updatesSee docs/demo-snapshot.md for an example snapshot captures.
kronictl start|stop|status|restart- daemon lifecycle.kronictl snapshot [--pretty]- fetch the latest snapshot via HTTP.kronictl watch [--pretty]- follow snapshot updates (SSE).kronictl monitor- interactive terminal UI (pressqto quit).kronictl tracker show [--watch]- inspect system-tracker metrics when the tracker is enabled in config.
HTTP (Unix socket):
GET /v1/snapshot-> JSON snapshotGET /v1/stream-> SSE stream of snapshots- Example (curl):
curl --unix-socket ~/.kronical/kronid.http.sock http://localhost/v1/snapshot
gRPC (Unix socket):
Snapshot-> snapshot payloadWatch-> streaming snapshotsGetSystemMetrics-> system tracker metrics- Example (grpcurl):
grpcurl -plaintext -import-path proto -proto proto/kroni.proto unix:///Users/$USER/.kronical/kronid.sock kroni.v1.Kroni/Snapshot
Snapshot payloads include current state, focus, cadence, counts, runId,
recent transitions, aggregated app/window durations, and the full in-memory
records list for the same run ID within the retention window.
Snapshot/stream payload coverage:
| Endpoint | Transport | Delivery | Payload | Notes |
|---|---|---|---|---|
GET /v1/snapshot |
HTTP (UDS) | one-shot | JSON snapshot | Latest snapshot for the current run ID within retention. |
GET /v1/stream |
HTTP SSE (UDS) | stream | JSON snapshot | Starts with the current snapshot, then emits updates. |
Snapshot |
gRPC (UDS) | one-shot | SnapshotReply |
Same snapshot data as HTTP. |
Watch |
gRPC (UDS) | stream | SnapshotReply |
Starts with the current snapshot, then emits updates. |
The event pipeline produces a deterministic state machine:
- States:
Active,Passive,Inactive,Locked. - Transitions respect
active_grace_secs(default 30s) andidle_threshold_secs(default 300s). - While locked, we emit lock/unlock signals and suppress raw input persistence.
State transition table:
| From | Trigger | Guard/Condition | To |
|---|---|---|---|
| Any | LockStart | — | Locked |
| Locked | LockEnd | Determine post-unlock state | Active/Passive/Inactive |
| Inactive/Passive | Keyboard/Mouse/App/Window/Pulse | — | Active |
| Active | Tick | no input for active_grace_secs |
Passive |
| Passive | Tick | no input for idle_threshold_secs |
Inactive |
User configuration lives at ~/.kronical/config.toml. Every field can be
overridden with a KRONICAL_ environment variable (for example,
KRONICAL_TRACKER_ENABLED=true). Relevant knobs include workspace paths,
retention, state thresholds, tracker cadence, and cache sizes. See
crates/common/src/config.rs for the full schema.
DuckDB is the default backend; SQLite remains available for lightweight setups.
Each backend runs in its own thread, draining persistence commands, committing
in batches, and reporting backlog metrics via the snapshot bus. The shared
logical schema covers raw_events, raw_envelopes, activity_records,
compact_events, and recent_transitions. Raw event identifiers embedded in
compact records use kronical_core::compression::RawEventPointer, which stores
the source table name so debug captures can live alongside the default
raw_events table without dangling references. Integration tests under
crates/storage/tests/ validate command execution, health reporting, and data
round trips.
crates/core::compression converts persisted raw events into compact summary
records. The engine maintains alignment by recording contributing raw event IDs
alongside structured payloads for scroll bursts, mouse trajectories, keyboard
activity, and focus transitions, using optional RawEventPointer metadata when
those debug records are retained.
- Prefer crate-scoped test runs (
cargo test -p kronical-core,cargo test -p kronical-storage, ...) to keep failures focused. - Tests must exercise observable behavior (state transitions, channel hand-offs, storage writes) rather than tautological assertions.
make coveragerunscargo llvm-covwith colorised output, writescoverage/summary.txt, and generates the HTML report undercoverage/html/.- Use
FEATURE_FLAGS="--features hotpath"duringmakeinvocations to enable optional profiling hooks, keeping runs single-threaded when necessary.
- Accessibility - required for global input hooks.
- Screen Recording - required for window title capture.
Grant both permissions to kronid (or the terminal hosting it) before starting
the daemon.
Active work targets pipeline coverage, storage verification, and UI consumers
of the snapshot endpoints. See docs/arch.md for internals.
