Local-first task monitoring for development work, humans, and coding agents.
Monitor aggregates updates from humans, Claude Code hooks, GitHub webhooks, and CLI/API clients into a task-centric dashboard and API. It stores durable state in SQLite, exposes REST and SSE endpoints, serves a lightweight web UI, and includes a Rust CLI for scripting and automation.
This project explores how engineering teams can track work across humans, coding agents, CI systems, and external tools without depending on a remote SaaS workflow system.
Early development. The core data model, REST API, SQLite storage, SSE streaming, CLI, and basic web UI are functional. APIs may still change.
- Rust workspace with separate common, server, and CLI crates
- Axum server with REST API, SSE streaming, and bearer-token auth
- SQLite persistence using
sqlx - Append-only task updates with durable sequence IDs for replay/resume
- Local-first workflow with optional strict auth for deployment
- Source adapters for manual updates, Claude Code hooks, and GitHub webhooks
- Lightweight web UI using Askama templates
- CLI designed for scripts, hooks, and agent workflows
CLI / Hooks / GitHub Webhooks / Manual Updates
|
v
Monitor Server
Axum + Services + Auth
|
v
SQLite
|
v
REST API + SSE + Web Dashboard
Monitor uses an append-only update model. External sources submit updates, the server stores them durably in SQLite, and clients consume current state through REST, live updates through SSE, or the web dashboard.
crates/monitor-commonShared domain types and API request/response structs.crates/monitor-serverAxum server, SQLite storage, SSE, Askama web UI, and source adapters.crates/monitor-cliCLI for creating workstreams/tasks and sending manual updates.
- Rust toolchain with
cargo - SQLite support is bundled through
sqlx/libsqlite3-sys - a browser if you want to use the web UI
Start the server:
cargo run -p monitor-serverBy default it listens on http://127.0.0.1:3000 and creates monitor.db in the repo root.
Install the CLI once:
cargo install --path crates/monitor-cliOpen the web UI:
http://127.0.0.1:3000/dashboard
Open the global live feed:
http://127.0.0.1:3000/stream
Create a workstream:
monitor-cli workstream create "Monitor MVP"Create a task:
monitor-cli task create "SSE replay support" \
--workstream <WORKSTREAM_ID>Send a manual update:
monitor-cli update manual \
--task <TASK_ID> \
--message "Implemented initial SSE path" \
--kind manual_note \
--level info \
--tags backend,sseList tasks:
monitor-cli task list- Track progress of several coding agents working on independent tasks.
- Send updates from Claude Code hooks into a shared local dashboard.
- Attach GitHub webhook updates to a workstream.
- Use the CLI from scripts to append task updates.
- Watch task progress live through SSE or the web UI.
The server reads configuration from environment variables:
MONITOR_BINDBind address. Default:127.0.0.1:3000MONITOR_DBSQLite URL. Default:sqlite:monitor.db?mode=rwcMONITOR_AUTH_MODErelaxedorstrictMONITOR_API_TOKENSComma-separated bearer tokens
Example:
export MONITOR_AUTH_MODE=strict
export MONITOR_API_TOKENS=dev-secret-token
cargo run -p monitor-serverAuth behavior:
relaxedRead endpoints may be open. Write endpoints require a token if tokens are configured.strictRead and write endpoints require a token. If no tokens are configured in strict mode, the server fails closed on API requests.
For CLI usage against an authenticated server:
export MONITOR_TOKEN=dev-secret-token
monitor-cli workstream listCommon commands:
# Workstreams
monitor-cli workstream create "My Workstream"
monitor-cli workstream list --include-archived
monitor-cli workstream update <WORKSTREAM_ID> --status archived
# Tasks
monitor-cli task create "My Task" --workstream <WORKSTREAM_ID>
monitor-cli task list --workstream <WORKSTREAM_ID>
monitor-cli task update <TASK_ID> --status blocked
monitor-cli task update <TASK_ID> --summary "Waiting on vendor response"
# Manual updates
monitor-cli update manual --task <TASK_ID> --message "Progress note"The CLI prints JSON responses so it can be used in scripts.
Global output flags:
--quiet, also available as--silent, suppresses all stdout/stderr output but preserves the normal exit status.
Claude hook style example:
monitor-cli --quiet update manual \
--task <TASK_ID> \
--message "Claude hook observed a session event" \
--kind claude_hook \
--tags claude,hook \
|| trueState endpoints:
POST /api/workstreamsGET /api/workstreamsPATCH /api/workstreams/:idPOST /api/tasksGET /api/tasksPATCH /api/tasks/:idGET /api/updatesGET /api/stream
Ingest endpoints:
POST /api/updates/manualPOST /api/updates/claude-hookPOST /api/updates/github-webhook
Useful query parameters:
GET /api/workstreams?include_archived=trueGET /api/tasks?workstream_id=<UUID>&status=activeGET /api/updates?task_id=<UUID>&kind=tool_use&limit=50GET /api/updates?tag=github&tag=completedRepeatedtagparams are match-any.GET /api/updates?after_seq=123GET /api/stream?task_id=<UUID>
GET /api/stream emits live events for task activity and state changes.
Behavior:
- task activity is emitted as
event: update - state changes are emitted as
task_created,task_updated,workstream_created, andworkstream_updated updateevents useUpdate.seqas the SSE event idLast-Event-IDis honored on reconnectafter_seqis also supported as a query param
Simple curl example:
curl -N http://127.0.0.1:3000/api/streamAuthenticated example:
curl -N \
-H "Authorization: Bearer $MONITOR_TOKEN" \
http://127.0.0.1:3000/api/streamSee docs/e2e-checklist.md for the full end-to-end verification procedure.
Run the full workspace tests:
cargo test --workspaceRun server-only tests:
cargo test -p monitor-server --bin monitor-serverUseful subsets:
cargo test -p monitor-server auth_tests
cargo test -p monitor-server integration_tests
cargo test -p monitor-server smoke_testNote:
- SSE and smoke tests bind a real loopback listener on
127.0.0.1:0 - those tests must be run in a normal local environment where opening local sockets is allowed
- they will fail in restricted sandboxes that deny loopback binds
Send raw hook payloads to:
POST /api/updates/claude-hook
The request must include:
task_idpayload
The hook caller is responsible for routing the event to the correct task_id.
Send raw GitHub webhook payloads to:
POST /api/updates/github-webhook
The request must include:
task_idpayload- optional selected headers such as
x-github-eventandx-github-delivery
This project is in early development. The core API and data model are functional but may change.
Design and implementation references:
- docs/plans/monitor-system-design.md
- docs/plans/monitor-implementation-plan.md
- ai_docs/source-event-shapes.md
This project is licensed under the MIT License.



