Skip to content

Release 0.2.1a1 - #70

Open
github-actions[bot] wants to merge 38 commits into
masterfrom
release-0.2.1a1
Open

Release 0.2.1a1#70
github-actions[bot] wants to merge 38 commits into
masterfrom
release-0.2.1a1

Conversation

@github-actions

Copy link
Copy Markdown

Human review requested!

JarbasAl and others added 30 commits June 18, 2025 02:15
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
The release used TigreGotico/gh-automations/publish-alpha.yml@master with inline 'python setup.py sdist bdist_wheel' on Python 3.14, failing with ModuleNotFoundError: setuptools — so every alpha release failed and merged dep fixes (ovos-bus-client<3.0.0) never reached PyPI. Switched to the standard OpenVoiceOS/gh-automations/publish-alpha.yml@dev (reads version from version.py, builds with python -m build), matching ovos-audio.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor: migrate packaging to pyproject.toml

Replace setup.py + requirements.txt + MANIFEST.in with a single pyproject.toml (PEP 621), dynamic version from ovos_messagebus/version.py. Verified the wheel builds with correct console scripts and bundled resources. (Release workflow already modernized on dev in a prior PR.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: use shared reusable build/publish workflows (pyproject)

Replace bespoke build_tests + install_tests + publish_stable (which ran 'python setup.py ...', now removed) with the shared OpenVoiceOS reusable workflows (build-tests/publish-stable @dev), matching ovos-core. build-tests builds via 'python -m build' and installs the wheel. Also removes the NeonGecko build-tests reusable (neongeckocom/.github) per the no-Neon policy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cit get_event_loop (#62)

tornado's IOLoop relies on there being a current asyncio event loop. On the
main thread, application.listen() implicitly created one via the deprecated
asyncio.get_event_loop() alias, and the daemon thread started
ioloop.IOLoop.instance().start() (also a deprecated alias) without ever
setting a loop on that thread. This logged
"DeprecationWarning: There is no current event loop" under
python -W always, and will break outright once get_event_loop() stops
creating implicit loops.

Move application.listen() and ioloop.IOLoop.current().start() into a single
function that runs entirely on the daemon thread, first creating and setting
an explicit asyncio event loop with asyncio.new_event_loop() /
asyncio.set_event_loop(). Behavior and public API are unchanged.
* fix: read ssl from the websocket config section

load_message_bus_config() read host/port/route from the `websocket`
config section but read `ssl` from the top-level config instead,
so the documented `websocket.ssl` key was never honoured.

ovos-bus-client does read `websocket.ssl` and builds a `wss://` URL
from it, so a user who sets the documented key ends up with clients
dialing wss:// against a server that still speaks plain ws:// -
breaking the install instead of encrypting it.

This does not add TLS termination (needs cert/key config that doesn't
exist yet); it fixes the key read and logs a warning at startup if
ssl resolves truthy, pointing at a reverse proxy as the supported way
to terminate TLS in front of this server.

* fix: remove misleading TLS-termination warning

websocket.ssl is a client-side setting consumed by ovos-bus-client to
build a wss:// URL. It is entirely valid to run ovos-messagebus behind
a TLS-terminating reverse proxy and set websocket.ssl: true so clients
dial wss://. The warning added in the previous commit assumed the
server itself must terminate TLS, which is false, and would fire on
every startup of a correctly configured reverse-proxy deployment,
telling the operator their working setup is broken. Remove it; the
underlying config-section fix (ssl read from `websocket`, not
top-level) stays.
* feat: serve wss:// when websocket.ssl is set

_run_bus() now passes ssl_options to Tornado's application.listen()
when websocket.ssl is truthy, so the server actually terminates TLS
itself instead of silently continuing to speak plain ws:// while
clients (per ovos-bus-client build_url()) dial wss://.

Certificate resolution, in order:
- websocket.ssl_cert / websocket.ssl_key if both are configured
- otherwise a self-signed pair is generated (or reused if already
  present) via ovos_utils.security.create_self_signed_cert() under
  $XDG_DATA_HOME/OpenVoiceOS/ovos-messagebus/certs

pyopenssl is not a hard dependency of ovos-messagebus or ovos-utils
(create_self_signed_cert requires it internally). It is imported
lazily only inside the ssl branch; if missing, a clear RuntimeError is
raised instead of silently falling back to plain ws://, since a
baffling TLS handshake failure at the client is worse than a loud
startup error at the server.

* refactor: bundle self-signed cert generator instead of ovos-utils

ovos_utils.security.create_self_signed_cert generates a 1024-bit
RSA/SHA-1 pair that OpenSSL 3 refuses to load (EE_KEY_TOO_SMALL), and
the helper is being removed from ovos-utils upstream. Add
ovos_messagebus/ssl_utils.py with an equivalent generator using the
cryptography library (RSA-2048/SHA-256, subjectAltName covering the
hostname/localhost/127.0.0.1, reuse-if-present), and point _get_ssl_options()
at it. Declare pyopenssl as an optional 'ssl' extra, matching the
[project.optional-dependencies] style introduced on PR #51.

* fix: declare cryptography (not pyopenssl) as the ssl extra

ssl_utils.py imports cryptography directly, not pyOpenSSL. Fix the
optional-dependency declaration and the RuntimeError/docstring text in
_get_ssl_options() to name the package actually imported and the
'ovos-messagebus[ssl]' extra that installs it. Collapse the two
byte-identical RuntimeError blocks into a shared
_missing_cryptography_error() helper. cryptography>=3.4 was verified
(not guessed) to support the x509.CertificateBuilder API surface used,
including timezone-aware not_valid_before/not_valid_after.
JarbasAl and others added 8 commits July 24, 2026 13:13
* benchmark: add live measurements for all three backends (Tornado, webrockets, Rust)

- Ran benchmark/run_benchmark.py at 4 load levels (5/20/50/100 clients)
  against Tornado, webrockets, and ovos-rust-messagebus v1.1.2
- Updated docs/backends.md: full 3-backend comparison tables with
  min/median/p95/p99/max latency columns; updated observations
- Updated FAQ.md: added Rust column to benchmark table, added
  "Rust connection saturation at 100 clients" Q&A
- Updated MAINTENANCE_REPORT.md with two transparency entries

Key results (vs Tornado baseline):
  5c×200m:   Rust +18%, webrockets +11%
  20c×1000m: Rust +20%, webrockets +9%
  50c×2000m: webrockets +24%, Rust +20%
  100c×500m: webrockets +4%, Rust ⚠ 28 conn errors

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: modernize workflows to latest gh-automations standards

New workflows added:
- unit_tests.yml  — test.yml@dev, triggers on push+PR to dev
- coverage.yml    — coverage.yml@dev with pr_comment
- pip_audit.yml   — pip-audit.yml@dev with pr_comment
- lint.yml        — lint.yml@dev (ruff) with pr_comment

Updated workflows:
- build_tests.yml: add python_versions matrix [3.10–3.13], pr_comment,
  trigger on dev push as well as master
- license_tests.yml: add secrets: inherit, fix empty `with:`, trigger on
  dev push as well as master

Removed:
- install_tests.yml (replaced by build_tests.yml matrix)

All workflows use @dev ref per workspace conventions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address all CodeRabbit review comments

Bug fixes:
- load_config.py: ssl=False override was silently ignored due to falsy
  `or` logic; replaced with `overrides['ssl'] if 'ssl' in overrides`
- event_handler.py: filter=True mode returned before broadcast on
  deserialization failure, dropping malformed frames; now logs at DEBUG
  and broadcasts raw payload unchanged (matches webrockets behavior)

Improvements:
- webrockets_backend.py: replace fixed 0.3s sleep with socket-based
  readiness probe (_wait_for_server_ready); add DEBUG log on
  deserialization failure instead of silent except
- requirements.txt: sync minimums to match pyproject.toml
  (ovos_bus_client >=1.3.0, ovos-utils >=0.8.0)

Tests (47 total, +20):
- test_event_handler.py: add TestOnMessage — filter=False/True with
  valid, malformed JSON, and non-OVOS dict payloads (4 new tests)
- test_load_config.py: add ssl override precedence regression tests
  (ssl=False wins, ssl=True wins, fallback to config) (3 new tests)
- test_webrockets_backend.py: add TestWaitForServerReady (port open,
  timeout graceful); update TestMain to patch _wait_for_server_ready
  instead of time; use sys.modules.setdefault for stub isolation
  (3 new tests, 2 updated)

Docs:
- docs/configuration.md: move ssl to top-level JSON example, add note
  that it is NOT nested under websocket
- docs/events.md: clarify filter mode delivery guarantee (malformed
  frames are still forwarded after fix)
- docs/server.md: fix "class-level set" → "module-level list"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: cross-reference defaults from ovos-config; fix ssl config key; add test extra

Bug fixes:
- load_config.py: ssl was read from config top-level (config.get('ssl'))
  but mycroft.conf defines it under websocket; now reads from
  websocket_configs.get('ssl') matching the actual default config

Docs corrected against ovos_config/mycroft.conf source of truth:
- docs/configuration.md: move ssl back under websocket in JSON example;
  correct host default 0.0.0.0 → 127.0.0.1; correct max_msg_size
  default 10 → 25 (per ovos_config/mycroft.conf); add citation to source
- docs/server.md: update ssl source citation

pyproject.toml:
- add [test] optional-dependency group with pytest>=7.0 and pytest-cov>=4.0
  so local test runs don't need external tooling

Tests:
- test_load_config.py: update VALID_WS_CONFIG to nest ssl under websocket
  (matching actual ovos-config defaults); update ssl regression tests to
  use websocket-nested ssl key; fix host assertion 0.0.0.0 → 127.0.0.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: drop maintenance report scratch file

* docs: reconcile SSL/TLS claims now that Tornado terminates wss:// itself

The Tornado backend now serves wss:// directly (websocket.ssl / ssl_cert /
ssl_key). Update the webrockets backend docstring, runtime warning, and
docs/backends.md, docs/configuration.md, docs/index.md, docs/server.md and
FAQ.md so none of them still claim or imply that no backend supports TLS,
and so operators are pointed at the Tornado backend as the real option for
server-terminated TLS instead of only a reverse proxy.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
webrockets cannot terminate TLS (no cert/key option in its Python API), so
_build_server() previously only logged a warning and started a plaintext
server while ovos-bus-client clients dial wss://. Raise a RuntimeError
instead, routed through main()'s existing error_hook/re-raise path, so the
server never starts and the failure is actionable: use the Tornado backend
(serves wss:// directly) or terminate TLS with a reverse proxy and leave
websocket.ssl unset for this process.

Also bound the unpinned webrockets dependency to >=0.2.1,<0.3.0 — verified
against upstream's 0.2.1 API docs that WebsocketServer, create_route,
route.connect/receive/disconnect and conn.broadcast are unchanged from what
this backend calls.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant