A broker-agnostic observability dashboard for Taskiq
fleets. Tasko ingests task lifecycle events through a TaskiqMiddleware, stores
them, and serves a live REST/WebSocket API consumed by a Next.js dashboard.
Prototype. v1 scope:
tasko-core+tasko-middleware+tasko-redis+web.
More screenshots
Overview β fleet health at a glance (chart + KPI panels are still wired to placeholder data):
Task detail β args, kwargs, result, and traceback for any run:
Workers β the live fleet, built from tasko-middleware heartbeats:
Queues β current depth per queue, read straight off the broker:
Schedules β recurring tasks the Taskiq scheduler is registered to kick, with next/last run:
| Path | What |
|---|---|
packages/tasko-core |
FastAPI server: ingest API, REST/WS, adapter registry |
packages/tasko-middleware |
TaskiqMiddleware subclass that reports events to core |
packages/tasko-redis |
BrokerAdapter implementation for Redis (v1 target) |
packages/tasko-rabbitmq |
BrokerAdapter stub (later / contributors) |
packages/tasko-nats |
BrokerAdapter stub (later / contributors) |
web/ |
Next.js SSR dashboard |
docker/ |
Multi-stage Dockerfiles + docker-compose.yml (dev) + docker-compose.prod.yml overlay |
packages/ and web/ are independent toolchains β uv never touches web/,
pnpm never touches packages/.
Point tasko-middleware at a running tasko-core β it's broker-agnostic, it
only reads what Taskiq hands every middleware hook:
from tasko_middleware import TaskoMiddleware
broker = broker.with_middlewares(TaskoMiddleware(core_url="http://localhost:8000"))That covers task lifecycle events and worker heartbeats. For the Schedules
view, wrap the ScheduleSource you already pass to TaskiqScheduler:
from taskiq import TaskiqScheduler
from taskiq.schedule_sources import LabelScheduleSource
from tasko_middleware import TaskoScheduleSource
scheduler = TaskiqScheduler(
broker,
sources=[TaskoScheduleSource(LabelScheduleSource(broker),
core_url="http://localhost:8000")],
)It forwards the schedule set to core on startup and whenever it changes;
the scheduler already stamps a schedule_id label on each kicked task, so
every run links back to its schedule through the normal event stream.
# install uv: https://docs.astral.sh/uv/getting-started/installation/
uv sync # one venv, one lockfile, whole workspace
uv run tasko-core # start the server on :8000
uv run tasko-seed # populate sample workers + tasks + schedules
uv run pytest # run the test suite
uv run ruff check . # lintcd web
pnpm install
pnpm dev # http://localhost:3000# dev stack β source is bind-mounted, core and web both hot-reload
docker compose -f docker/docker-compose.yml up --build
# dashboard http://localhost:3100
# API http://localhost:8100
# postgres localhost:5434 (tasko / tasko)
# production-style stack β built images, no mounts, restart policy
docker compose -f docker/docker-compose.yml -f docker/docker-compose.prod.yml up --build -d
# sample data for the dashboard
docker compose -f docker/docker-compose.yml exec core tasko-seedAfter changing a dependency (package.json / pyproject.toml), rebuild:
docker compose -f docker/docker-compose.yml up --build --renew-anon-volumes.





