stemtrace 0.3.0__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. {stemtrace-0.3.0 → stemtrace-0.3.2}/.bumpversion.toml +1 -1
  2. {stemtrace-0.3.0 → stemtrace-0.3.2}/CHANGELOG.md +42 -1
  3. {stemtrace-0.3.0 → stemtrace-0.3.2}/PKG-INFO +87 -5
  4. {stemtrace-0.3.0 → stemtrace-0.3.2}/README.md +86 -4
  5. {stemtrace-0.3.0 → stemtrace-0.3.2}/docker-compose.yml +1 -1
  6. {stemtrace-0.3.0 → stemtrace-0.3.2}/examples/celery_app.py +35 -5
  7. {stemtrace-0.3.0 → stemtrace-0.3.2}/examples/fastapi_integration.py +7 -11
  8. {stemtrace-0.3.0 → stemtrace-0.3.2}/examples/with_auth.py +2 -3
  9. stemtrace-0.3.2/examples/with_login.py +43 -0
  10. {stemtrace-0.3.0 → stemtrace-0.3.2}/pyproject.toml +2 -1
  11. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/__init__.py +115 -2
  12. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/library/transports/redis.py +29 -0
  13. stemtrace-0.3.2/src/stemtrace/server/__main__.py +250 -0
  14. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/api/routes.py +195 -29
  15. stemtrace-0.3.2/src/stemtrace/server/api/websocket.py +49 -0
  16. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/fastapi/extension.py +33 -3
  17. stemtrace-0.3.2/src/stemtrace/server/fastapi/form_auth.py +157 -0
  18. stemtrace-0.3.2/src/stemtrace/server/fastapi/login_routes.py +157 -0
  19. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/fastapi/router.py +3 -1
  20. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/package.json +1 -1
  21. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/static.py +115 -15
  22. stemtrace-0.3.2/src/stemtrace/server/ui/templates/login.html +153 -0
  23. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/integration/test_workers_refresh.py +1 -1
  24. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_examples.py +50 -2
  25. stemtrace-0.3.2/tests/unit/test_form_auth.py +135 -0
  26. stemtrace-0.3.2/tests/unit/test_form_login.py +169 -0
  27. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_init.py +94 -0
  28. stemtrace-0.3.2/tests/unit/test_routes.py +1186 -0
  29. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_transports.py +38 -1
  30. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_ui_static_prefix.py +13 -0
  31. {stemtrace-0.3.0 → stemtrace-0.3.2}/uv.lock +1 -1
  32. stemtrace-0.3.0/src/stemtrace/server/__main__.py +0 -136
  33. stemtrace-0.3.0/src/stemtrace/server/api/websocket.py +0 -28
  34. stemtrace-0.3.0/tests/unit/test_routes.py +0 -575
  35. {stemtrace-0.3.0 → stemtrace-0.3.2}/.dockerignore +0 -0
  36. {stemtrace-0.3.0 → stemtrace-0.3.2}/.github/dependabot.yml +0 -0
  37. {stemtrace-0.3.0 → stemtrace-0.3.2}/.github/workflows/ci.yml +0 -0
  38. {stemtrace-0.3.0 → stemtrace-0.3.2}/.github/workflows/e2e.yml +0 -0
  39. {stemtrace-0.3.0 → stemtrace-0.3.2}/.github/workflows/release.yml +0 -0
  40. {stemtrace-0.3.0 → stemtrace-0.3.2}/.gitignore +0 -0
  41. {stemtrace-0.3.0 → stemtrace-0.3.2}/.pre-commit-config.yaml +0 -0
  42. {stemtrace-0.3.0 → stemtrace-0.3.2}/.python-version +0 -0
  43. {stemtrace-0.3.0 → stemtrace-0.3.2}/CONTRIBUTING.md +0 -0
  44. {stemtrace-0.3.0 → stemtrace-0.3.2}/Dockerfile +0 -0
  45. {stemtrace-0.3.0 → stemtrace-0.3.2}/Dockerfile.e2e +0 -0
  46. {stemtrace-0.3.0 → stemtrace-0.3.2}/LICENSE +0 -0
  47. {stemtrace-0.3.0 → stemtrace-0.3.2}/Makefile +0 -0
  48. {stemtrace-0.3.0 → stemtrace-0.3.2}/build_ui.py +0 -0
  49. {stemtrace-0.3.0 → stemtrace-0.3.2}/docker-compose.e2e.rabbitmq.yml +0 -0
  50. {stemtrace-0.3.0 → stemtrace-0.3.2}/docker-compose.e2e.yml +0 -0
  51. {stemtrace-0.3.0 → stemtrace-0.3.2}/docker-compose.rabbitmq.yml +0 -0
  52. {stemtrace-0.3.0 → stemtrace-0.3.2}/docs/screenshots/task_details.png +0 -0
  53. {stemtrace-0.3.0 → stemtrace-0.3.2}/docs/screenshots/unregistered.png +0 -0
  54. {stemtrace-0.3.0 → stemtrace-0.3.2}/docs/screenshots/workflow.png +0 -0
  55. {stemtrace-0.3.0 → stemtrace-0.3.2}/progress.md +0 -0
  56. {stemtrace-0.3.0 → stemtrace-0.3.2}/scripts/wait_for_http.py +0 -0
  57. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/core/__init__.py +0 -0
  58. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/core/events.py +0 -0
  59. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/core/exceptions.py +0 -0
  60. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/core/graph.py +0 -0
  61. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/core/ports.py +0 -0
  62. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/library/__init__.py +0 -0
  63. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/library/bootsteps.py +0 -0
  64. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/library/config.py +0 -0
  65. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/library/scrubbing.py +0 -0
  66. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/library/signals.py +0 -0
  67. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/library/transports/__init__.py +0 -0
  68. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/library/transports/memory.py +0 -0
  69. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/library/transports/rabbitmq.py +0 -0
  70. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/py.typed +0 -0
  71. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/__init__.py +0 -0
  72. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/api/__init__.py +0 -0
  73. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/api/schemas.py +0 -0
  74. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/consumer.py +0 -0
  75. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/fastapi/__init__.py +0 -0
  76. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/fastapi/auth.py +0 -0
  77. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/store.py +0 -0
  78. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/__init__.py +0 -0
  79. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/biome.json +0 -0
  80. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/index.html +0 -0
  81. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/package-lock.json +0 -0
  82. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/playwright.config.ts +0 -0
  83. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/postcss.config.js +0 -0
  84. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/api/client.ts +0 -0
  85. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/api/queries.ts +0 -0
  86. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/DateRangePicker.tsx +0 -0
  87. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/Filters.tsx +0 -0
  88. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/TaskGraph.tsx +0 -0
  89. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/TaskList.tsx +0 -0
  90. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/TaskStateBadge.tsx +0 -0
  91. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/TaskTimeline.tsx +0 -0
  92. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/TimelineBar.tsx +0 -0
  93. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/components/Tooltip.tsx +0 -0
  94. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/hooks/WebSocketContext.tsx +0 -0
  95. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/hooks/useInfiniteScroll.ts +0 -0
  96. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/index.css +0 -0
  97. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/main.tsx +0 -0
  98. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/__root.tsx +0 -0
  99. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/graph.$rootId.tsx +0 -0
  100. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/graphs.tsx +0 -0
  101. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/index.tsx +0 -0
  102. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/registry.tsx +0 -0
  103. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/tasks.$taskId.tsx +0 -0
  104. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/routes/workers.tsx +0 -0
  105. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/utils/format.ts +0 -0
  106. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/src/vite-env.d.ts +0 -0
  107. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/fixtures/mock-api.ts +0 -0
  108. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/fixtures/mock-data.ts +0 -0
  109. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/graphs.spec.ts +0 -0
  110. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/registry.spec.ts +0 -0
  111. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/task-detail.spec.ts +0 -0
  112. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/tasks.spec.ts +0 -0
  113. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tests/workers.spec.ts +0 -0
  114. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tsconfig.json +0 -0
  115. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/tsconfig.tsbuildinfo +0 -0
  116. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/ui/frontend/vite.config.ts +0 -0
  117. {stemtrace-0.3.0 → stemtrace-0.3.2}/src/stemtrace/server/websocket.py +0 -0
  118. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/__init__.py +0 -0
  119. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/conftest.py +0 -0
  120. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/e2e/__init__.py +0 -0
  121. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/e2e/conftest.py +0 -0
  122. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/e2e/tasks.py +0 -0
  123. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/e2e/test_api_e2e.py +0 -0
  124. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/e2e/test_workers_e2e.py +0 -0
  125. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/integration/__init__.py +0 -0
  126. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/integration/test_embedded_static.py +0 -0
  127. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/integration/test_fastapi_integration.py +0 -0
  128. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/integration/test_rabbitmq_transport.py +0 -0
  129. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/integration/test_redis_transport.py +0 -0
  130. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/integration/test_static_ui.py +0 -0
  131. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/__init__.py +0 -0
  132. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_auth.py +0 -0
  133. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_bootsteps.py +0 -0
  134. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_config.py +0 -0
  135. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_consumer.py +0 -0
  136. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_events.py +0 -0
  137. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_exceptions.py +0 -0
  138. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_graph.py +0 -0
  139. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_scrubbing.py +0 -0
  140. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_signals.py +0 -0
  141. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_store.py +0 -0
  142. {stemtrace-0.3.0 → stemtrace-0.3.2}/tests/unit/test_websocket.py +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.bumpversion]
2
- current_version = "0.3.0"
2
+ current_version = "0.3.2"
3
3
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
4
4
  serialize = ["{major}.{minor}.{patch}"]
5
5
  commit = true
@@ -7,10 +7,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.2] - 2026-02-28
11
+
12
+ ### Fixed
13
+ - Redis transport: normalize `ssl_cert_reqs` URL parameter from `CERT_REQUIRED`/`CERT_NONE`/`CERT_OPTIONAL` to lowercase values expected by redis-py
14
+
15
+ ## [0.3.1] - 2026-01-04
16
+
17
+ ### Added
18
+ - UI: optional built-in form login flow for protecting the stemtrace UI
19
+
20
+ ### Changed
21
+ - Config: split server broker URL from worker event transport URL (`broker_url` vs `transport_url`)
22
+ - API: avoid blocking worker/registry refresh operations
23
+ - Docs: clarify optional configuration and retention behavior
24
+
25
+ ### Fixed
26
+ - Workers/Registry: refresh via Celery inspect to reduce stale worker/task registry data
27
+
10
28
  ## [0.3.0] - 2026-01-04
11
29
 
30
+ ### Added
31
+ - RabbitMQ event transport
32
+
33
+ ### Changed
34
+ - Tests: improved RabbitMQ transport coverage and reduced duplication
35
+
36
+ ### Fixed
37
+ - UI: harden derived prefix sanitization
38
+ - CI/E2E: avoid artifact name conflicts
39
+
12
40
  ## [0.2.2] - 2026-01-03
13
41
 
42
+ ### Added
43
+ - README: add release version badge
44
+
45
+ ### Changed
46
+ - Packaging/docs: simplify badges and metadata
47
+ - README: pin PyPI badge to release version
48
+ - Tests: make version assertion dynamic
49
+
50
+ ### Fixed
51
+ - Version bump: sync fix
52
+
14
53
  ## [0.2.1] - 2026-01-03
15
54
 
16
55
  ### Added
@@ -63,7 +102,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
63
102
  - **E2E test suite**: Docker API tests + Playwright browser tests
64
103
  - **Comprehensive test suite**: 350+ Python tests, 90%+ coverage
65
104
 
66
- [unreleased]: https://github.com/iansokolskyi/stemtrace/compare/v0.2.1...HEAD
105
+ [unreleased]: https://github.com/iansokolskyi/stemtrace/compare/v0.3.0...HEAD
106
+ [0.3.0]: https://github.com/iansokolskyi/stemtrace/compare/v0.2.2...v0.3.0
107
+ [0.2.2]: https://github.com/iansokolskyi/stemtrace/compare/v0.2.1...v0.2.2
67
108
  [0.2.1]: https://github.com/iansokolskyi/stemtrace/compare/v0.2.0...v0.2.1
68
109
  [0.2.0]: https://github.com/iansokolskyi/stemtrace/compare/v0.1.1...v0.2.0
69
110
  [0.1.1]: https://github.com/iansokolskyi/stemtrace/compare/v0.1.0...v0.1.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stemtrace
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: A lightweight Celery task flow visualizer
5
5
  Project-URL: Homepage, https://github.com/iansokolskyi/stemtrace
6
6
  Project-URL: Documentation, https://github.com/iansokolskyi/stemtrace#readme
@@ -56,7 +56,7 @@ Description-Content-Type: text/markdown
56
56
 
57
57
  **Zero-infrastructure Celery task flow visualizer**
58
58
 
59
- [![PyPI version](https://img.shields.io/badge/pypi-v0.3.0-darklime)](https://pypi.org/project/stemtrace)
59
+ [![PyPI version](https://img.shields.io/badge/pypi-v0.3.2-darklime)](https://pypi.org/project/stemtrace)
60
60
  [![Python](https://img.shields.io/pypi/pyversions/stemtrace.svg)](https://pypi.org/project/stemtrace/)
61
61
  [![CI](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml/badge.svg)](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml)
62
62
  [![codecov](https://codecov.io/gh/iansokolskyi/stemtrace/graph/badge.svg)](https://codecov.io/gh/iansokolskyi/stemtrace)
@@ -302,11 +302,33 @@ batch_processor
302
302
  - **Timing** — Each node displays start time and duration directly in the graph
303
303
  - **Aggregate state** — Container shows running/success/failure based on member states
304
304
 
305
- ### Environment Variables
305
+ ### Environment Variables (Optional)
306
+
307
+ You **do not need** these environment variables if you’re using the Python APIs:
308
+ - `stemtrace.init_worker(app, transport_url=..., prefix=..., ttl=...)`
309
+ - `stemtrace.init_app(app, broker_url=..., transport_url=...)`
310
+
311
+ They exist mainly as **convenient defaults** for:
312
+ - `stemtrace server` / `stemtrace consume`
313
+ - container/Docker setups where passing flags is awkward
314
+
315
+ If your app already uses env vars like `BROKER_URL`, `REDIS_URL`, etc., just pass them through:
316
+
317
+ ```python
318
+ import os
319
+ import stemtrace
320
+
321
+ stemtrace.init_app(
322
+ app,
323
+ broker_url=os.environ["BROKER_URL"],
324
+ transport_url=os.getenv("STEMTRACE_TRANSPORT_URL") or os.environ["BROKER_URL"],
325
+ )
326
+ ```
306
327
 
307
328
  | Variable | Description | Default |
308
329
  |----------|-------------|---------|
309
- | `STEMTRACE_BROKER_URL` | Broker URL for `stemtrace server` / `stemtrace consume` and `stemtrace.init_app()` | `redis://localhost:6379/0` |
330
+ | `STEMTRACE_BROKER_URL` | Celery broker URL (used for on-demand worker/registry inspection). Also used as the default for `STEMTRACE_TRANSPORT_URL`. | `redis://localhost:6379/0` |
331
+ | `STEMTRACE_TRANSPORT_URL` | Event transport URL (where stemtrace publishes/consumes events). | Defaults to `STEMTRACE_BROKER_URL`. |
310
332
 
311
333
  ### Supported Brokers
312
334
 
@@ -315,6 +337,24 @@ batch_processor
315
337
  | Redis | `redis://`, `rediss://` | ✅ Supported |
316
338
  | RabbitMQ | `amqp://`, `amqps://`, `pyamqp://` | ✅ Supported |
317
339
 
340
+ ### Event Retention & Server Restarts (Important)
341
+
342
+ stemtrace builds the UI from **events** and keeps state in an **in-memory** graph store.
343
+ That means a `stemtrace server` restart starts from an empty store and only becomes “full”
344
+ again once events are re-consumed.
345
+
346
+ - **Redis (Streams)**: On restart, the server can rebuild state by replaying events that are
347
+ still retained in the stream (bounded by `ttl` / stream trimming).
348
+ - **RabbitMQ (fanout + per-consumer queue)**:
349
+ - Events already consumed/acked by the server are **gone**.
350
+ - Events published while the server is down are only visible after restart if the server’s
351
+ durable per-consumer queue still exists and the messages are still within TTL.
352
+ - **Workers + Registry tabs**: stemtrace uses **Celery inspect** on demand to populate workers
353
+ and registered tasks, so those pages work even if the server missed `worker_ready` events.
354
+ - **If you need durable history across restarts**: point stemtrace events at Redis even if your
355
+ Celery broker is RabbitMQ (set `transport_url` in `stemtrace.init_worker(...)` and set
356
+ `STEMTRACE_TRANSPORT_URL` for the server / embedded setup).
357
+
318
358
  ## 🐳 Docker
319
359
 
320
360
  ```bash
@@ -331,6 +371,15 @@ docker run -p 8000:8000 \
331
371
  ghcr.io/iansokolskyi/stemtrace
332
372
  ```
333
373
 
374
+ RabbitMQ broker + Redis event transport (recommended if you want history across server restarts):
375
+
376
+ ```bash
377
+ docker run -p 8000:8000 \
378
+ -e STEMTRACE_BROKER_URL=amqp://guest:guest@host.docker.internal:5672// \
379
+ -e STEMTRACE_TRANSPORT_URL=redis://host.docker.internal:6379/0 \
380
+ ghcr.io/iansokolskyi/stemtrace
381
+ ```
382
+
334
383
  Or with Docker Compose:
335
384
 
336
385
  ```yaml
@@ -371,11 +420,22 @@ Open [http://localhost:8000](http://localhost:8000) to view the dashboard.
371
420
  ```bash
372
421
  stemtrace server \
373
422
  --broker-url redis://myredis:6379/0 \
423
+ --transport-url redis://myredis:6379/0 \
374
424
  --host 0.0.0.0 \
375
425
  --port 8000 \
376
426
  --reload # For development
377
427
  ```
378
428
 
429
+ #### Protecting the Server (Built-in Login Page)
430
+
431
+ ```bash
432
+ stemtrace server \
433
+ --broker-url redis://myredis:6379/0 \
434
+ --login-username admin \
435
+ --login-password secret \
436
+ --login-secret change-me
437
+ ```
438
+
379
439
  #### High-Scale Production Setup
380
440
 
381
441
  Note: `stemtrace server` includes an embedded consumer today (single-process). A multi-process deployment mode is planned.
@@ -397,6 +457,8 @@ That's it. `init_app()` automatically:
397
457
  - Mounts the dashboard at `/stemtrace/`
398
458
  - Starts the event consumer
399
459
 
460
+ Tip: you can also set `transport_url` if you want stemtrace events stored separately from your Celery broker.
461
+
400
462
  #### Configuration Options
401
463
 
402
464
  ```python
@@ -406,6 +468,7 @@ import stemtrace
406
468
  extension = stemtrace.init_app(
407
469
  app,
408
470
  broker_url="redis://localhost:6379/0",
471
+ transport_url=None, # Defaults to broker_url
409
472
  prefix="/stemtrace", # Mount path AND event stream prefix (normalized)
410
473
  ttl=86400, # Event TTL in seconds
411
474
  max_nodes=10000, # Max nodes in memory
@@ -425,7 +488,26 @@ from your_app.auth import require_admin
425
488
  stemtrace.init_app(app, broker_url="redis://localhost:6379/0", auth_dependency=Depends(require_admin))
426
489
  ```
427
490
 
428
- Or use built-in auth helpers:
491
+ #### With Built-in Login Page (Recommended for UI-first)
492
+
493
+ If you primarily use the UI, the easiest way to protect **UI + assets + API + WebSocket**
494
+ is the built-in form login (cookie session):
495
+
496
+ ```python
497
+ import stemtrace
498
+
499
+ stemtrace.init_app(
500
+ app,
501
+ broker_url="redis://localhost:6379/0",
502
+ login_username="admin",
503
+ login_password="secret",
504
+ login_secret="change-me", # recommended for production
505
+ )
506
+ ```
507
+
508
+ This serves a sign-in page at `/stemtrace/login` and protects everything under `/stemtrace`.
509
+
510
+ #### Built-in Auth Helpers (Basic / API key)
429
511
 
430
512
  ```python
431
513
  import stemtrace
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Zero-infrastructure Celery task flow visualizer**
4
4
 
5
- [![PyPI version](https://img.shields.io/badge/pypi-v0.3.0-darklime)](https://pypi.org/project/stemtrace)
5
+ [![PyPI version](https://img.shields.io/badge/pypi-v0.3.2-darklime)](https://pypi.org/project/stemtrace)
6
6
  [![Python](https://img.shields.io/pypi/pyversions/stemtrace.svg)](https://pypi.org/project/stemtrace/)
7
7
  [![CI](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml/badge.svg)](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml)
8
8
  [![codecov](https://codecov.io/gh/iansokolskyi/stemtrace/graph/badge.svg)](https://codecov.io/gh/iansokolskyi/stemtrace)
@@ -248,11 +248,33 @@ batch_processor
248
248
  - **Timing** — Each node displays start time and duration directly in the graph
249
249
  - **Aggregate state** — Container shows running/success/failure based on member states
250
250
 
251
- ### Environment Variables
251
+ ### Environment Variables (Optional)
252
+
253
+ You **do not need** these environment variables if you’re using the Python APIs:
254
+ - `stemtrace.init_worker(app, transport_url=..., prefix=..., ttl=...)`
255
+ - `stemtrace.init_app(app, broker_url=..., transport_url=...)`
256
+
257
+ They exist mainly as **convenient defaults** for:
258
+ - `stemtrace server` / `stemtrace consume`
259
+ - container/Docker setups where passing flags is awkward
260
+
261
+ If your app already uses env vars like `BROKER_URL`, `REDIS_URL`, etc., just pass them through:
262
+
263
+ ```python
264
+ import os
265
+ import stemtrace
266
+
267
+ stemtrace.init_app(
268
+ app,
269
+ broker_url=os.environ["BROKER_URL"],
270
+ transport_url=os.getenv("STEMTRACE_TRANSPORT_URL") or os.environ["BROKER_URL"],
271
+ )
272
+ ```
252
273
 
253
274
  | Variable | Description | Default |
254
275
  |----------|-------------|---------|
255
- | `STEMTRACE_BROKER_URL` | Broker URL for `stemtrace server` / `stemtrace consume` and `stemtrace.init_app()` | `redis://localhost:6379/0` |
276
+ | `STEMTRACE_BROKER_URL` | Celery broker URL (used for on-demand worker/registry inspection). Also used as the default for `STEMTRACE_TRANSPORT_URL`. | `redis://localhost:6379/0` |
277
+ | `STEMTRACE_TRANSPORT_URL` | Event transport URL (where stemtrace publishes/consumes events). | Defaults to `STEMTRACE_BROKER_URL`. |
256
278
 
257
279
  ### Supported Brokers
258
280
 
@@ -261,6 +283,24 @@ batch_processor
261
283
  | Redis | `redis://`, `rediss://` | ✅ Supported |
262
284
  | RabbitMQ | `amqp://`, `amqps://`, `pyamqp://` | ✅ Supported |
263
285
 
286
+ ### Event Retention & Server Restarts (Important)
287
+
288
+ stemtrace builds the UI from **events** and keeps state in an **in-memory** graph store.
289
+ That means a `stemtrace server` restart starts from an empty store and only becomes “full”
290
+ again once events are re-consumed.
291
+
292
+ - **Redis (Streams)**: On restart, the server can rebuild state by replaying events that are
293
+ still retained in the stream (bounded by `ttl` / stream trimming).
294
+ - **RabbitMQ (fanout + per-consumer queue)**:
295
+ - Events already consumed/acked by the server are **gone**.
296
+ - Events published while the server is down are only visible after restart if the server’s
297
+ durable per-consumer queue still exists and the messages are still within TTL.
298
+ - **Workers + Registry tabs**: stemtrace uses **Celery inspect** on demand to populate workers
299
+ and registered tasks, so those pages work even if the server missed `worker_ready` events.
300
+ - **If you need durable history across restarts**: point stemtrace events at Redis even if your
301
+ Celery broker is RabbitMQ (set `transport_url` in `stemtrace.init_worker(...)` and set
302
+ `STEMTRACE_TRANSPORT_URL` for the server / embedded setup).
303
+
264
304
  ## 🐳 Docker
265
305
 
266
306
  ```bash
@@ -277,6 +317,15 @@ docker run -p 8000:8000 \
277
317
  ghcr.io/iansokolskyi/stemtrace
278
318
  ```
279
319
 
320
+ RabbitMQ broker + Redis event transport (recommended if you want history across server restarts):
321
+
322
+ ```bash
323
+ docker run -p 8000:8000 \
324
+ -e STEMTRACE_BROKER_URL=amqp://guest:guest@host.docker.internal:5672// \
325
+ -e STEMTRACE_TRANSPORT_URL=redis://host.docker.internal:6379/0 \
326
+ ghcr.io/iansokolskyi/stemtrace
327
+ ```
328
+
280
329
  Or with Docker Compose:
281
330
 
282
331
  ```yaml
@@ -317,11 +366,22 @@ Open [http://localhost:8000](http://localhost:8000) to view the dashboard.
317
366
  ```bash
318
367
  stemtrace server \
319
368
  --broker-url redis://myredis:6379/0 \
369
+ --transport-url redis://myredis:6379/0 \
320
370
  --host 0.0.0.0 \
321
371
  --port 8000 \
322
372
  --reload # For development
323
373
  ```
324
374
 
375
+ #### Protecting the Server (Built-in Login Page)
376
+
377
+ ```bash
378
+ stemtrace server \
379
+ --broker-url redis://myredis:6379/0 \
380
+ --login-username admin \
381
+ --login-password secret \
382
+ --login-secret change-me
383
+ ```
384
+
325
385
  #### High-Scale Production Setup
326
386
 
327
387
  Note: `stemtrace server` includes an embedded consumer today (single-process). A multi-process deployment mode is planned.
@@ -343,6 +403,8 @@ That's it. `init_app()` automatically:
343
403
  - Mounts the dashboard at `/stemtrace/`
344
404
  - Starts the event consumer
345
405
 
406
+ Tip: you can also set `transport_url` if you want stemtrace events stored separately from your Celery broker.
407
+
346
408
  #### Configuration Options
347
409
 
348
410
  ```python
@@ -352,6 +414,7 @@ import stemtrace
352
414
  extension = stemtrace.init_app(
353
415
  app,
354
416
  broker_url="redis://localhost:6379/0",
417
+ transport_url=None, # Defaults to broker_url
355
418
  prefix="/stemtrace", # Mount path AND event stream prefix (normalized)
356
419
  ttl=86400, # Event TTL in seconds
357
420
  max_nodes=10000, # Max nodes in memory
@@ -371,7 +434,26 @@ from your_app.auth import require_admin
371
434
  stemtrace.init_app(app, broker_url="redis://localhost:6379/0", auth_dependency=Depends(require_admin))
372
435
  ```
373
436
 
374
- Or use built-in auth helpers:
437
+ #### With Built-in Login Page (Recommended for UI-first)
438
+
439
+ If you primarily use the UI, the easiest way to protect **UI + assets + API + WebSocket**
440
+ is the built-in form login (cookie session):
441
+
442
+ ```python
443
+ import stemtrace
444
+
445
+ stemtrace.init_app(
446
+ app,
447
+ broker_url="redis://localhost:6379/0",
448
+ login_username="admin",
449
+ login_password="secret",
450
+ login_secret="change-me", # recommended for production
451
+ )
452
+ ```
453
+
454
+ This serves a sign-in page at `/stemtrace/login` and protects everything under `/stemtrace`.
455
+
456
+ #### Built-in Auth Helpers (Basic / API key)
375
457
 
376
458
  ```python
377
459
  import stemtrace
@@ -44,7 +44,7 @@ services:
44
44
  # depends_on:
45
45
  # redis:
46
46
  # condition: service_healthy
47
- # command: ["consume", "--broker-url", "redis://redis:6379"]
47
+ # command: ["consume", "--transport-url", "redis://redis:6379"]
48
48
 
49
49
  volumes:
50
50
  redis-data:
@@ -9,18 +9,38 @@ task events for visualization, including:
9
9
  - Group/chord visualization with synthetic GROUP nodes
10
10
 
11
11
  Usage:
12
- # Install dependencies
13
- pip install stemtrace[redis]
12
+ # Install dependencies (PyPI)
13
+ pip install stemtrace
14
+
15
+ # Or (from repo)
16
+ uv sync --extra dev
14
17
 
15
18
  # Start Redis
16
19
  docker run -d -p 6379:6379 redis:alpine
17
20
 
21
+ # (Optional) Start RabbitMQ (for RabbitMQ broker tests)
22
+ docker run -d -p 5672:5672 -p 15672:15672 rabbitmq:3-management
23
+
18
24
  # Start worker
19
25
  celery -A examples.celery_app worker --loglevel=info
20
26
 
21
27
  # Start stemtrace server (in another terminal)
22
28
  stemtrace server
23
29
 
30
+ # Switch between Redis and RabbitMQ using environment variables:
31
+ # - CELERY_BROKER_URL controls where Celery sends tasks (default: Redis)
32
+ # - CELERY_RESULT_BACKEND controls result backend (default: Redis)
33
+ #
34
+ # RabbitMQ note: chord-style workflows require a result backend that supports
35
+ # chords; `redis://` works well.
36
+ #
37
+ # Example (RabbitMQ broker + Redis result backend):
38
+ # export CELERY_BROKER_URL="amqp://guest:guest@localhost:5672//"
39
+ # export CELERY_RESULT_BACKEND="redis://localhost:6379/1"
40
+ #
41
+ # (Optional) override stemtrace's event transport independent of Celery:
42
+ # export STEMTRACE_TRANSPORT_URL="amqp://guest:guest@localhost:5672//"
43
+
24
44
  # Run demo tasks
25
45
  python examples/celery_app.py workflow # Complex workflow (chain + group)
26
46
  python examples/celery_app.py group # Parallel group (synthetic GROUP node)
@@ -32,6 +52,7 @@ Usage:
32
52
 
33
53
  from __future__ import annotations
34
54
 
55
+ import os
35
56
  import random
36
57
  import sys
37
58
  from typing import Any
@@ -40,15 +61,24 @@ from celery import Celery, chain, chord, group
40
61
 
41
62
  import stemtrace
42
63
 
64
+ # Defaults (Redis local)
65
+ _DEFAULT_REDIS_BROKER = "redis://localhost:6379/0"
66
+ _DEFAULT_REDIS_BACKEND = "redis://localhost:6379/1"
67
+
68
+ # Allow env-driven configuration so the same example can test Redis or RabbitMQ.
69
+ _CELERY_BROKER_URL = os.getenv("CELERY_BROKER_URL", _DEFAULT_REDIS_BROKER)
70
+ _CELERY_RESULT_BACKEND = os.getenv("CELERY_RESULT_BACKEND", _DEFAULT_REDIS_BACKEND)
71
+
43
72
  # Create Celery app
44
73
  app = Celery(
45
74
  "examples",
46
- broker="redis://localhost:6379/0",
47
- backend="redis://localhost:6379/1",
75
+ broker=_CELERY_BROKER_URL,
76
+ backend=_CELERY_RESULT_BACKEND,
48
77
  )
49
78
 
50
79
  # Initialize stemtrace tracking
51
- stemtrace.init_worker(app)
80
+ _STEMTRACE_TRANSPORT_URL = os.getenv("STEMTRACE_TRANSPORT_URL")
81
+ stemtrace.init_worker(app, transport_url=_STEMTRACE_TRANSPORT_URL)
52
82
 
53
83
 
54
84
  # =============================================================================
@@ -5,7 +5,7 @@ This example shows how to mount stemtrace as a router in your existing
5
5
  FastAPI app, with an embedded consumer for development.
6
6
 
7
7
  Usage:
8
- pip install stemtrace[server]
8
+ pip install stemtrace
9
9
  uvicorn examples.fastapi_integration:app --reload
10
10
  """
11
11
 
@@ -14,19 +14,22 @@ from fastapi import FastAPI
14
14
  import stemtrace
15
15
 
16
16
  # Configuration
17
+ #
18
+ # `broker_url` is Celery's broker (used for on-demand worker inspection).
19
+ # `transport_url` is where stemtrace consumes events from (defaults to broker_url).
17
20
  BROKER_URL = "redis://localhost:6379/0"
21
+ TRANSPORT_URL = None # e.g. "redis://localhost:6379/0"
18
22
 
19
23
  # Create FastAPI app
20
24
  app = FastAPI(
21
25
  title="My App with stemtrace",
22
26
  )
23
27
 
24
- # Initialize stemtrace in one line (convenience API)
28
+ # Initialize stemtrace in one line
25
29
  stemtrace.init_app(
26
30
  app,
27
31
  broker_url=BROKER_URL,
28
- embedded_consumer=True, # Run consumer in background
29
- serve_ui=True, # Serve the React UI
32
+ transport_url=TRANSPORT_URL,
30
33
  )
31
34
 
32
35
 
@@ -40,13 +43,6 @@ async def root() -> dict[str, str]:
40
43
  @app.get("/health")
41
44
  async def health() -> dict[str, str]:
42
45
  """Health check endpoint."""
43
- # Note: With convenience API, you don't have access to the extension instance
44
- # Use StemtraceExtension directly if you need programmatic access:
45
- #
46
- # from stemtrace import StemtraceExtension
47
- # flow = StemtraceExtension(broker_url=...)
48
- # flow.init_app(app)
49
- # return {"status": flow.consumer.is_running}
50
46
  #
51
47
  return {"status": "ok"}
52
48
 
@@ -5,7 +5,7 @@ This example shows how to protect stemtrace endpoints with
5
5
  basic authentication or API key authentication.
6
6
 
7
7
  Usage:
8
- pip install stemtrace[server]
8
+ pip install stemtrace
9
9
  uvicorn examples.with_auth:app --reload
10
10
  """
11
11
 
@@ -23,11 +23,10 @@ app = FastAPI(
23
23
  title="stemtrace with Auth",
24
24
  )
25
25
 
26
- # Initialize stemtrace with authentication (convenience API)
26
+ # Initialize stemtrace with authentication
27
27
  stemtrace.init_app(
28
28
  app,
29
29
  broker_url=BROKER_URL,
30
- embedded_consumer=True,
31
30
  auth_dependency=require_basic_auth(AUTH_USERNAME, AUTH_PASSWORD),
32
31
  )
33
32
 
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env python3
2
+ """Example: stemtrace with built-in form login.
3
+
4
+ This example shows how to protect the stemtrace UI with a pretty login page.
5
+
6
+ Usage:
7
+ pip install stemtrace[server]
8
+ uvicorn examples.with_login:app --reload
9
+ """
10
+
11
+ from fastapi import FastAPI
12
+
13
+ import stemtrace
14
+
15
+ # Configuration
16
+ BROKER_URL = "redis://localhost:6379/0"
17
+ LOGIN_USERNAME = "admin"
18
+ LOGIN_PASSWORD = "secret" # NOSONAR - example credential for local demo only
19
+ LOGIN_SECRET = "change-me" # recommended for production
20
+
21
+ app = FastAPI(title="stemtrace with Login")
22
+
23
+ stemtrace.init_app(
24
+ app,
25
+ broker_url=BROKER_URL,
26
+ embedded_consumer=True,
27
+ serve_ui=True,
28
+ login_username=LOGIN_USERNAME,
29
+ login_password=LOGIN_PASSWORD,
30
+ login_secret=LOGIN_SECRET,
31
+ )
32
+
33
+
34
+ @app.get("/")
35
+ async def root() -> dict[str, str]:
36
+ """Public endpoint."""
37
+ return {"message": "Visit /stemtrace (login required)"}
38
+
39
+
40
+ if __name__ == "__main__":
41
+ import uvicorn
42
+
43
+ uvicorn.run(app, host="0.0.0.0", port=8000)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stemtrace"
3
- version = "0.3.0"
3
+ version = "0.3.2"
4
4
  description = "A lightweight Celery task flow visualizer"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -76,6 +76,7 @@ build-backend = "hatchling.build"
76
76
  packages = ["src/stemtrace"]
77
77
  artifacts = [
78
78
  "src/stemtrace/server/ui/frontend/dist/**",
79
+ "src/stemtrace/server/ui/templates/**",
79
80
  ]
80
81
 
81
82
  [tool.hatch.build.targets.wheel.hooks.custom]