lynx-runner with the Basilisk engine, streaming telemetry, and retrieving per-job trajectory artifacts.
The scenario used here is scenario_basic_orbit_lynx.py.
What Lynx handles for you
You bring the simulation. Lynx handles everything around it.| Handled by Lynx | Provided by you | |
|---|---|---|
| Run configurations | Generated from your dispersion spec | |
| Job distribution | Queued and routed across your runner pool | |
| Retries | Automatic, up to the limit you set | |
| Output directory | Created and isolated per run | |
| Telemetry collection | Ingested and stored | Streamed from your scenario (optional) |
| Early termination | Watchdog monitors and kills on breach | Stopping conditions defined in your config |
| Simulation physics | Your code and models | |
| Result files | Written to the directory Lynx provides |
How the runner launches your scenario
For each job, the Basilisk engine plugin inlynx-runner does the following:
- Creates a per-job output directory
- Starts a gRPC telemetry server on a dynamic local port
- Spawns your Python entrypoint as a subprocess with these environment variables injected:
| Variable | Description |
|---|---|
TETRYX_RUN_CONFIG | Path to a JSON file containing the sampled run configuration for this job |
TETRYX_TELEMETRY_URL | gRPC endpoint to send scalar telemetry to |
TETRYX_OUTPUT_DIR | Directory where your scenario should write artifacts |
TETRYX_RUN_CONFIG and does not need to know or hardcode the backing location.
- Waits for the process to exit
- If an early termination condition fires, sends SIGKILL to the subprocess immediately
- Collects output files from
TETRYX_OUTPUT_DIRand attaches them to the job result
The run configuration
TETRYX_RUN_CONFIG points to a JSON file with this shape:
parameters keys and values come directly from the dispersions you defined in your batch config. Lynx samples them using the method you specified (Latin Hypercube Sampling in this tutorial).
The scenario
scenarios/aerospace/python/scenario_basic_orbit_lynx.py reads the run config, builds a headless Basilisk simulation, steps through the orbit, and emits scalar telemetry at each time step.
Reading the run config:
TETRYX_TELEMETRY_URL is not set the client silently no-ops. Your scenario will still run and produce artifacts.
Writing artifacts:
The scenario writes three files to TETRYX_OUTPUT_DIR before exiting:
| File | Contents |
|---|---|
summary.json | Final orbital state, sample count, execution time, success flag |
trajectory.csv | Time-series of position and velocity vectors (one row per 10-second step) |
orbit_position.png | Position components in km over time, normalised to orbital periods |
output_files attached to the job result, retrievable via GET /jobs/{job_id}/results.
Dispersed parameters
The tutorial config (scenarios/aerospace/configs/basic_orbit_tutorial.yaml) disperses these orbital elements using uniform distributions:
| Parameter | Range | Units |
|---|---|---|
semi_major_axis_m | 6 950 000 – 7 050 000 | m |
eccentricity | 0.00005 – 0.002 | dimensionless |
inclination_deg | 30 – 36 | deg |
raan_deg | 40 – 55 | deg |
arg_perigee_deg | 330 – 355 | deg |
true_anomaly_deg | 70 – 100 | deg |
duration_orbits | 0.70 – 0.80 | orbits |
Runner configuration
The runner needs to know which Python executable to use, where the scenario entrypoint lives, and where to write per-job output. These are set in the runner’s YAML config file. A minimal config for this tutorial (configs/lynx-runner-basilisk.yaml):
| Environment variable | Equivalent config field |
|---|---|
LYNX_RUNNER_ENGINES | engines |
LYNX_RUNNER_CAPABILITIES | capabilities |
LYNX_RUNNER_PYTHON_EXECUTABLE | python_executable |
LYNX_RUNNER_BASILISK_ENTRYPOINT | basilisk_entrypoint |
LYNX_RUNNER_OUTPUT_ROOT | output_root |
LYNX_RUNNER_WORKING_DIRECTORY | working_directory |
LYNX_RUNNER_MAX_CONCURRENT_JOBS | max_concurrent_jobs |
Start a Basilisk runner
Submit the batch
Monitor progress
Retrieve results
Once the batch status isCompleted, fetch all job results:
output_files paths are on the runner’s local filesystem. Artifact upload to S3 (and retrieval via the API) is a planned feature.
Early termination
Early termination lets you kill a simulation mid-run when a condition is met, without waiting for the job timeout. The runner’s watchdog monitors telemetry and sends SIGKILL to the subprocess if a condition fires. To enable it in the batch config:orbit.radius_km drops below 6371 km (Earth’s mean radius), the runner kills the subprocess and marks the job failed.
Adapting this scenario to your simulation
The integration contract your scenario must satisfy:- Read
TETRYX_RUN_CONFIGat startup — this is the path to your sampled parameter set - Write artifacts to
TETRYX_OUTPUT_DIRbefore exiting - Exit 0 on success, non-zero on failure
- Optionally stream scalar telemetry to
TETRYX_TELEMETRY_URLduring execution
What comes next
- Connect a GitHub Actions workflow that submits a batch on push and fails CI if jobs fail
- Expand to 500+ runs with
"sampling_method": "latin_hypercube"and a fixed seed for reproducible studies - Add a second runner type (e.g.
px4_sitl) using the same control plane and API - Enable artifact upload to S3 for cross-machine artifact retrieval (planned)