{"id":51279466,"url":"https://github.com/ilovepixelart/matador","last_synced_at":"2026-06-30T00:32:25.918Z","repository":{"id":363651963,"uuid":"1263101869","full_name":"ilovepixelart/matador","owner":"ilovepixelart","description":"An async dashboard for toro queues.","archived":false,"fork":false,"pushed_at":"2026-06-23T20:15:58.000Z","size":1223,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-30T00:32:21.893Z","etag":null,"topics":["dashboard","fastapi","htmx","jobs","monitoring","python","queue","redis","toro"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ilovepixelart.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-08T16:12:53.000Z","updated_at":"2026-06-23T20:12:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ilovepixelart/matador","commit_stats":null,"previous_names":["ilovepixelart/matador"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ilovepixelart/matador","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilovepixelart%2Fmatador","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilovepixelart%2Fmatador/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilovepixelart%2Fmatador/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilovepixelart%2Fmatador/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ilovepixelart","download_url":"https://codeload.github.com/ilovepixelart/matador/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ilovepixelart%2Fmatador/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34948227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-29T02:00:05.398Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dashboard","fastapi","htmx","jobs","monitoring","python","queue","redis","toro"],"created_at":"2026-06-30T00:32:24.664Z","updated_at":"2026-06-30T00:32:25.909Z","avatar_url":"https://github.com/ilovepixelart.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# matador 🗡️\n\nA live, **server-rendered** dashboard for [toro](https://github.com/ilovepixelart/toro)\nqueues - watch queues, inspect jobs, and act on them (retry, remove, promote,\npause…) from the browser.\n\n[![Python](https://img.shields.io/pypi/pyversions/matador-dashboard)](https://pypi.org/project/matador-dashboard/)\n\\\n[![PyPI](https://img.shields.io/pypi/v/matador-dashboard)](https://pypi.org/project/matador-dashboard/)\n[![Downloads](https://static.pepy.tech/badge/matador-dashboard)](https://pepy.tech/project/matador-dashboard)\n[![License](https://img.shields.io/github/license/ilovepixelart/matador)](https://github.com/ilovepixelart/matador/blob/main/LICENSE)\n\\\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=ilovepixelart_matador\u0026metric=coverage)](https://sonarcloud.io/summary/new_code?id=ilovepixelart_matador)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ilovepixelart_matador\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ilovepixelart_matador)\n\\\n[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=ilovepixelart_matador\u0026metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=ilovepixelart_matador)\n[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=ilovepixelart_matador\u0026metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=ilovepixelart_matador)\n[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=ilovepixelart_matador\u0026metric=security_rating)](https://sonarcloud.io/summary/new_code?id=ilovepixelart_matador)\n\n![matador dashboard](https://raw.githubusercontent.com/ilovepixelart/matador/main/docs/dashboard.png)\n\n```bash\npip install matador-dashboard   # the import name is `matador`\n```\n\n\u003e Installed as **`matador-dashboard`** on PyPI (the name `matador` was taken), but\n\u003e you `import matador`.\n\n## What it is\n\nFastAPI + Jinja on the server, **HTMX + Tailwind** on the page - no SPA, no build\nstep to run. Every queue / tab / page is a real URL, so reload, back/forward and\ndeep-links all work. It reads straight from Redis through toro's async API.\n\n## Features\n\n- **Queues sidebar** with per-state counts, a **per-queue activity sparkline**\n  (last hour, all queues on one shared scale, failures in red), and **state\n  tabs** (active / waiting / delayed / completed / failed) that swap the job\n  list over HTMX.\n- **Health chips** per queue: latency (age of the next job in line, warns past\n  30s), completed/failed counts with failure share, and average duration over\n  the last hour - server-rendered SVG, no chart library.\n- **Flows** - toro's parent/child job trees, shown root-first: a flow is its\n  root job moving through the normal tabs (parked roots fold into active), with\n  children hidden from the lists and a recursive tree on the job detail with\n  per-node status pills and fan-in progress that counts completions only;\n  destructive actions warn that removing a parent takes its whole subtree. The\n  active tab also carries a **flow-throughput strip** - whole flows completed/\n  failed over the last hour, with end-to-end flow-duration percentiles.\n- **Job detail** lazy-loaded on expand: data, options, return value, timings,\n  logs, and stack traces - syntax-highlighted server-side (Pygments, no client JS).\n- **Search** within a state by job id or a name/data substring.\n- **Live updates** over SSE - counts refresh as jobs complete, no reload.\n- **Actions**: pause/resume a queue, retry/remove/promote a job, retry-all,\n  clean a state, and schedulers (run-now / remove) - each behind a styled confirm\n  dialog (not `window.confirm`).\n- **Numbered pagination**, a **Redis health bar** (memory, clients, eviction\n  policy), and a persistent **dark / light** theme.\n\n## Run it\n\n```bash\nuv run python scripts/seed.py             # optional: populate demo data\nuv run uvicorn scripts.run:app --reload   # http://localhost:8000\n```\n\n`scripts/run.py` watches a few demo queues; edit the list there, or wire it up yourself.\n\n## Integrate into an existing app\n\nmatador is an ASGI app - `mount` it into your FastAPI/Starlette service at any path.\nURLs are `root_path`-aware (Starlette `url_for`), so a sub-path mount just works.\n\n```python\nfrom fastapi import Depends\nfrom matador import create_app\n\napp.mount(\n    \"/toro\",\n    create_app(\n        [\"emails\", \"billing\"],\n        connection=redis,                       # share your existing redis pool\n        dependencies=[Depends(require_admin)],  # protect it with your auth\n    ),\n)\n```\n\n- **Mount anywhere** - links, static assets and the SSE stream all carry the mount\n  prefix automatically; works behind a path-stripping reverse proxy too.\n- **`connection=`** - pass your `redis.asyncio.Redis` so matador shares your pool\n  (it never closes a connection it didn't open). Omit it to open its own from `url=`.\n  This is also the right way to embed: a mounted sub-app's lifespan doesn't run, so\n  the host should own the connection.\n- **`dependencies=`** - applied to every route, so your app's auth gates the\n  dashboard. (The `/static` mount isn't covered - wrap the whole mount if the assets\n  themselves need auth.)\n- Other stacks (Django, Flask, non-Python): run matador standalone and reverse-proxy.\n\n## Security\n\nmatador ships no auth of its own - it inherits the host app's via\n`dependencies=`, or sits behind an authenticating reverse proxy. An app built\nwithout `dependencies` logs a warning at startup, because every route\n(including delete/retry/pause) is open to whoever can reach it.\n\n- **CSRF**: the same-origin guard turns on automatically when `dependencies`\n  are configured (auth usually means cookies, and cookies are what make CSRF\n  real). Pass `require_same_origin=` explicitly to override.\n- **Stack traces** are shown in job detail by default and can leak source\n  paths or secrets from exception messages - `show_stacktraces=False` hides\n  them when the audience shouldn't see internals.\n- The dashboard can do whatever its Redis connection can do; scope that Redis\n  user/network accordingly. See toro's `docs/security.md` for the queue-side\n  model.\n\nStandalone is just the no-extras case:\n\n```python\napp = create_app([\"emails\", \"billing\"], url=\"redis://localhost:6379\")\n```\n\nIt serves **HTML** (an HTMX UI), not a JSON API - point a browser at it.\n\n## Develop\n\nManaged with [uv](https://astral.sh/uv); the Astral toolchain throughout.\n\n```bash\nuv sync                          # venv + deps + dev group\nuv run ruff check . \u0026\u0026 uv run ruff format .   # lint (strict) + format\nuv run ty check                  # type check\nuv run pytest                    # unit + integration (needs Redis on :6379)\nuv run pytest -m e2e             # Playwright browser tests (run separately)\n\n# rebuild the stylesheet while editing templates/styles\n./tailwindcss -i styles/input.css -o matador/static/app.css --watch\n```\n\n## License\n\n[MIT](https://github.com/ilovepixelart/matador/blob/main/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filovepixelart%2Fmatador","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Filovepixelart%2Fmatador","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Filovepixelart%2Fmatador/lists"}