Skip to main content

starting-nats-server

Use when Lightfall code (demos, integration tests, scripts) needs a running NATS broker and one isn't already up — covers binary lookup, subprocess spawn, readiness probe, and teardown.

Source facts

Repository
als-controls/lightfall
Last source activity
June 2, 2026 at 23:53
Detected SKILL.md language
English
Stars
8
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
starting-nats-server
description
Use when Lightfall code (demos, integration tests, scripts) needs a running NATS broker and one isn't already up — covers binary lookup, subprocess spawn, readiness probe, and teardown.
# Starting a NATS server for Lightfall Lightfall does not embed NATS. In production it's an external service; in tests/demos you spawn `nats-server` as a subprocess. This skill captures the recipe. ## When to use - Writing an integration test that needs a real broker (mocks won't catch wire bugs). - Writing a demo or smoke script that exercises the IPC layer end-to-end. - A new dev environment can't connect to `nats://localhost:4222` and you need a local one for development. ## When NOT to use - Unit tests — use the `NatsLink` abstraction / fake transport instead. - Production / staging — NATS is an external service. Ask beamline controls for the URL; don't bundle a broker into the Lightfall process. ## Binary location Order of resolution (use this exact order — matches the existing scripts): 1. `$NATS_SERVER_BIN` env var, if set. 2. `shutil.which("nats-server")` — picks it up if on `$PATH`. 3. Hardcoded Windows winget install path (last resort, used by integration tests): ``` C:\Users\rp\AppData\Local\Microsoft\WinGet\Packages\NATSAuthors.NATSServer_Microsoft.Winget.Source_8wekyb3d8bbwe\nats-server-v2.10.25-windows-amd64\nats-server.exe ``` Install on Windows: `winget install NATSAuthors.NATSServer`. The binary lands at the path above. After install, either add it to `$PATH` or set `$NATS_SERVER_BIN`. ## Canonical pytest fixture This is the pattern used in `tests/integration/test_tsuchinoko_e2e.py` and `tests/integration/test_pipelines_e2e.py`. Copy it; don't reinvent. ```python import os import shutil import socket import subprocess import time import pytest def _free_port() -> int: with socket.socket() as s: s.bind(("127.0.0.1", 0)) return s.getsockname()[1] def _resolve_nats_bin() -> str | None: return os.environ.get("NATS_SERVER_BIN") or shutil.which("nats-server") @pytest.fixture(scope="session") def nats_server(): """Start a nats-server on a random port. Session-scoped. Skips if no binary.""" bin_path = _resolve_nats_bin() if not bin_path or not os.path.isfile(bin_path): pytest.skip("nats-server not found (set NATS_SERVER_BIN or install on PATH)") port = _free_port() proc = subprocess.Popen( [bin_path, "-p", str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) deadline = time.monotonic() + 10 while time.monotonic() < deadline: try: socket.create_connection(("127.0.0.1", port), timeout=1).close() break except OSError: time.sleep(0.1) else: proc.kill() pytest.fail("nats-server did not start within 10s") yield f"nats://127.0.0.1:{port}" proc.terminate() try: proc.wait(timeout=5) except subprocess.TimeoutExpired: proc.kill() ``` ## One-shot script form For demos / smoke scripts (see `scripts/demo_autonomous_experiment.py`): ```python import os, shutil, subprocess, sys, time if os.environ.get("NATS_TEST_AUTOSTART") == "1": bin_path = os.environ.get("NATS_SERVER_BIN") or shutil.which("nats-server") if not bin_path: print("ERROR: NATS_SERVER_BIN not set and nats-server not on PATH.") sys.exit(2) nats_proc = subprocess.Popen([bin_path, "-p", "4222"]) time.sleep(1.0) # cheap readiness; prefer the socket probe above for tests ``` Default port `4222` is fine for single-broker scripts. Use a random free port (see `_free_port` above) when tests run in parallel or alongside a real broker. ## Gotchas - **Naive `time.sleep(1.0)` is flaky under load.** Use the socket-connect probe in the canonical fixture for anything that has to be reliable. - **Always tear down.** A leaked `nats-server` on port 4222 will make every subsequent test fixture *appear* to work but connect to a stale broker with the wrong subjects. Use the `yield` + `terminate` pattern, not bare `Popen`. - **Don't redirect stdout/stderr to PIPE without draining** for long-running spawns — the OS pipe buffer (~64 KB on Windows) fills up and nats-server blocks. For session-scoped fixtures that finish in seconds this is fine; for anything longer, redirect to `subprocess.DEVNULL` or a file. - **JetStream is off by default.** If you need JS, add `-js` and a `-sd <storage-dir>` flag. None of the current Lightfall tests need it. ## Reference implementations in this repo - `tests/integration/test_tsuchinoko_e2e.py` — canonical fixture (copied above). - `tests/integration/test_pipelines_e2e.py` — same pattern, slightly different skip condition. - `scripts/demo_autonomous_experiment.py` — minimal one-shot form gated by `NATS_TEST_AUTOSTART=1`. If you're writing a new test that needs NATS, copy from the first one.
View on GitHub