bluesky-nats
bluesky-nats connects Bluesky document streams to NATS JetStream.
It supports two primary workflows:
- publish Bluesky documents from a
RunEngineto JetStream subjects - consume JetStream subjects and dispatch documents to Bluesky callbacks
Who this is for
Use this package if you want to move Bluesky documents across processes or hosts using NATS JetStream while preserving document semantics.
Installation
Install from PyPI:
uv add bluesky-nats
Install with optional dependencies used by the examples:
uv add bluesky-nats --extra examples
Python support: >=3.11,<3.15
Prerequisites
You need a NATS server with JetStream enabled and a stream configured for your
subject pattern (for example events.>).
By default, this package uses stream name bluesky.
For local setup, use the provided Docker assets:
docker/compose.yamldocker/Readme.adoc(includes stream creation commands)
Quick start (local)
-
Start NATS JetStream (from this repository root):
cd docker docker compose up -d
-
Ensure a JetStream named
blueskyexists and is bound toevents.>. Seedocker/Readme.adocfor exact commands. -
In a separate shell from repository root, run examples:
uv run python examples/print_subscriber.py uv run python examples/publisher.py
-
You should see published documents printed by the subscriber.
Minimal publisher example
Attach NATSPublisher to a RunEngine and publish documents:
import atexit
from bluesky.run_engine import RunEngine
from bluesky_nats.nats_client import NATSClientConfig, connect_sync
from bluesky_nats.nats_executor import AsyncPublishManager, CoroutineExecutor
from bluesky_nats.nats_publisher import NATSPublisher
if __name__ == "__main__":
RE = RunEngine({})
config = NATSClientConfig(servers=["nats://localhost:4222"])
executor = CoroutineExecutor()
client, js = connect_sync(executor, config)
manager = AsyncPublishManager(executor, client, strict_publish=True)
nats_publisher = NATSPublisher(
manager=manager,
js=js,
subject_factory="events.nats-bluesky",
)
if not client.is_connected:
raise RuntimeError("NATS connection is required before starting plans")
RE.subscribe(nats_publisher)
# Optional: expose current publisher status snapshot
print(nats_publisher.health)
# Optional: register lifecycle cleanup (useful for interactive sessions)
atexit.register(
nats_publisher.shutdown_callback(timeout=10, shutdown_executor=True)
)
Minimal dispatcher/consumer example
Consume subjects and forward documents to a callback:
import asyncio
from bluesky_nats.nats_dispatcher import NATSDispatcher
async def main() -> None:
async with NATSDispatcher(subject="events.>") as dispatcher:
dispatcher.subscribe(print)
await asyncio.sleep(60)
if __name__ == "__main__":
asyncio.run(main())
Configuration
- Client connectivity is configured through
NATSClientConfig. - Publisher subjects are derived as
<subject_factory>.<document_name>. - Publisher does not pick a stream explicitly; the server maps subjects to streams, while JetStream publish acknowledgements confirm server receipt.
strict_publish=TrueonAsyncPublishManagerenables fail-fast behavior: async publish failures are latched and raised on subsequent callback calls.publisher.healthreturns aPublisherHealthsnapshot (connectivity, pending publishes, last error/ack, and last subject).publisher.shutdown_callback(...)returns a zero-arg callable suitable foratexit.register(...).
Strict mode
Use strict mode when message delivery is mandatory for your architecture.
- Set
strict_publish=TrueonAsyncPublishManager. - Check
client.is_connectedbefore running plans to gate execution. - If an async publish fails later, strict mode raises
RuntimeErrorfrom the callback path so the RunEngine can fail fast.
See the examples/ directory for complete runnable scripts.
Troubleshooting
NoStreamResponseError: the target stream does not exist or does not match your subject pattern.- Subscriber receives nothing: verify publisher and dispatcher subjects are
compatible (for example
events.nats-blueskyvsevents.>). - Connection failures: check server URL/port and whether JetStream is enabled.
Metadata
Release files for bluesky-nats 2.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| bluesky_nats-2.0.1.tar.gz | 188.9 kB | Without provenance | Details for bluesky_nats-2.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| bluesky_nats-2.0.1-py3-none-any.whl | Python 3 | none | any | Details for bluesky_nats-2.0.1-py3-none-any.whl |
Total release size: 227.7 kB
Release files / bluesky_nats-2.0.1.tar.gz
| Download URL | bluesky_nats-2.0.1.tar.gz |
|---|---|
| Size | 188.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
043ae85e5078535b62e3402753e65ed430a034dd284d6b9985e45882d736459a
|
|
BLAKE2b-256 checksum How to use checksums |
809364e64865697f00f5da86f87e7d9a2b86bf52c938c0d6ec8aaba440fbb397
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Release files / bluesky_nats-2.0.1-py3-none-any.whl
| Download URL | bluesky_nats-2.0.1-py3-none-any.whl |
|---|---|
| Size | 38.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
77c86727b72f815b29751ea54bbfbd36e29e85ab047a0e7685bd4e3747854db4
|
|
BLAKE2b-256 checksum How to use checksums |
cb6dd1e941fdd578cbe9809e667ec8e3b1277fcab737f24eebedbc13fb476f1f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Security for bluesky-nats 2.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.