Starlight is a high-frequency workflow engine written in Rust. It runs asynchronous tasks connected by named channels, letting you build pipelines that generate, transform, route, and deliver data.
Define a workflow in YAML or JSON, then manage it through the HTTP API or CLI. Workflows form directed acyclic graphs (DAGs): tasks run concurrently and pass JSON messages downstream through buffered channels.
Development status: Starlight is still under active development. Features, configuration formats, and APIs may change as the project evolves.
- Generate data from timers, random numbers, CSV files, or simulated signals.
- Filter, map, convert, split, and aggregate messages as they flow through tasks.
- Send results to logs, CSV files, or HTTP endpoints.
- Start, pause, resume, and stop workflows, and inspect per-task state and metrics.
- Generate workflow configurations interactively using an LLM.
- Extend the engine with custom Rust tasks.
You need Rust and Cargo with support for the Rust 2024 edition. Run the following commands from the repository root.
cargo build --workspace
RUST_LOG=info cargo run --bin starlightThe API listens on port 8246 by default. Leave the server running while using
the CLI in another terminal.
Save this as demo.yaml. It generates a random integer between 1 and 100 every
500 milliseconds and logs each message in the server terminal.
id: demo
name: Random Number Demo
description: Generate random numbers and log them.
channel_buffer_size: 1000
tasks:
- id: generator
type: number_generator
params:
min: 1
max: 100
interval_ms: 500
dependencies: []
outputs:
out: [numbers]
- id: log
type: logger
params:
prefix: "[demo]"
level: info
dependencies: [numbers]
outputs: {}The generator writes to the channel numbers; the logger receives from that
same channel. A source has no dependencies, and a sink has no outputs.
In a second terminal, open the repository root and create a shell alias for the
CLI. The executable currently builds as cli; the alias lets you use sl in the
commands below.
alias sl="$PWD/target/debug/cli"
sl push demo.yaml # Save the configuration on the server
sl run demo # Mount the saved workflow and start it
sl list # List loaded workflows
sl state demo # Inspect task state and metrics
sl pause demo
sl start demo # Resume a paused workflow
sl stop demoYou can also invoke the CLI through Cargo without an alias:
cargo run -p cli -- listThe CLI uses http://localhost:8246 when no context is selected. See
Configuration to connect to another server.
Workflow files and loaded workflows are managed separately:
- Push saves a configuration to disk without loading or starting it.
- Mount loads a saved configuration into the engine without starting it.
- Run mounts and starts a workflow in one command.
- Unmount unloads a workflow while keeping its saved file.
- Remove deletes the saved file and unloads the workflow if it is loaded.
Files are stored in .starlight/engine/workflows/ by default, relative to the
server's working directory. Files remain on disk after a server restart; mount
and start them again to run them.
| Command | Purpose |
|---|---|
sl list |
List loaded workflows |
sl list --all |
List saved workflow files with their loaded status |
sl push <file.yaml> |
Save a YAML or JSON configuration on the server |
sl pull <id> |
Download a configuration as <id>.yaml |
sl pull <id> --output json |
Download a configuration as <id>.json |
sl mount <id> |
Load a saved workflow |
sl unmount <id> |
Unload a workflow and keep its file |
sl run <id> |
Mount and start a workflow |
sl start <id> |
Start, resume, or restart a loaded workflow |
sl pause <id> |
Pause a workflow |
sl stop <id> |
Stop a workflow |
sl state <id> |
Show per-task state and metrics |
sl remove <id> |
Delete a saved workflow and unload it |
sl generate |
Open the interactive workflow generation chat |
Restarting a stopped workflow creates fresh task instances and channels, so its task state is reset. Resuming a paused workflow continues with its existing tasks.
Each workflow has an id, a name, and a list of tasks. The description and
channel_buffer_size fields are optional; the default channel buffer size is
1000.
Each task specifies a unique id, a registered type, task-specific params,
input dependencies, and outputs. Channel IDs are shared across the workflow:
connect tasks by using the same channel ID in an output and a dependency. The
engine supports fan-out to multiple consumers and merging multiple input channels.
A flat dependency list uses the default input port, in. Tasks that support
multiple input ports can use a named mapping:
dependencies:
left: [channel_a]
right: [channel_b]When a workflow is loaded, the engine checks for unknown task types, duplicate task IDs, unresolved dependencies, and cycles.
| Task type | Purpose |
|---|---|
number_generator |
Emit random integers at a configured interval |
timer |
Emit a payload on an interval or cron schedule |
csv_reader |
Stream rows from a CSV file |
csv_writer |
Write messages to a CSV file |
logger |
Log incoming messages |
filter |
Pass or drop messages based on field conditions |
splitter |
Route messages to outputs based on field values |
json_mapper |
Project or rename JSON fields |
type_converter |
Convert field types |
math_exp_eval |
Evaluate expressions and add result fields |
aggregator |
Compute windowed count, sum, average, min, max, or collections |
http_sender |
Send messages to an HTTP endpoint |
dummy |
No-op task for testing |
simulator |
Generate composite signals such as sine waves, trends, and anomalies |
Task parameters and examples are documented in the
built-in task implementations. The simulator task is
registered by the server; its signal models are described in the
simulator README.
Workflow generation is optional and requires a running LLM endpoint that supports
the OpenAI-compatible /chat/completions API. Configure LLM_BASE_URL and
LLM_MODEL on the server, then run:
sl generateDescribe the workflow you want and answer any follow-up questions. Press
Ctrl+S to save the latest generated configuration as <id>.yaml in the current
directory, and Esc to leave the chat. Use sl push <id>.yaml and sl run <id>
to save and run it on the server.
You can also generate a workflow through the API:
curl -X POST http://localhost:8246/workflows/generate \
-H 'Content-Type: application/json' \
-d '{
"prompt": "Generate a random integer between 1 and 100 every second and log it",
"model": "qwen3.5-9b",
"base_url": "http://localhost:1234/v1",
"auto_load": false
}'The server validates generated configurations and feeds errors back to the model
for repair, with up to three total attempts. Responses have a status of
questions, completed, or validation_failed. To continue a conversation,
send the returned messages history with your next user message instead of
prompt. Setting auto_load to true loads a valid configuration into the
engine; start it separately through the workflow API.
The CLI uses the same API that you can call from scripts or other applications.
Paths below assume the default empty BASE_PATH.
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
Check server health |
| GET | /workflows |
List loaded workflows |
| GET | /workflows/{id} |
Get workflow information |
| GET | /workflows/{id}/state |
Get per-task state and metrics |
| PATCH | /workflows/{id} |
Send a start, pause, or stop command |
| POST | /workflows/{id}/mount |
Load a saved workflow |
| POST | /workflows/{id}/unmount |
Unload a workflow |
| GET | /workflows/files |
List saved workflow files |
| POST | /workflows/files |
Save a configuration as YAML; request body is JSON |
| GET | /workflows/files/{id} |
Download a saved configuration as JSON |
| DELETE | /workflows/files/{id} |
Delete a saved file and unload its workflow |
| POST | /workflows/generate |
Generate a workflow with an LLM |
For example, pause the demo workflow:
curl -X PATCH http://localhost:8246/workflows/demo \
-H 'Content-Type: application/json' \
-d '{"command": "pause"}'The server reads environment variables and loads a .env file when present.
| Variable | Default | Purpose |
|---|---|---|
PORT |
8246 |
API server port |
BASE_PATH |
Empty | Prefix for all API routes, such as /api |
ENGINE_DIR |
.starlight/engine |
Server storage directory; workflows live in its workflows/ subdirectory |
LLM_MODEL |
qwen3.5-9b |
Default model for workflow generation |
LLM_BASE_URL |
http://localhost:1234/v1 |
Default LLM API base URL |
CLI_DIR |
.starlight/cli |
CLI configuration directory, resolved relative to the user's home directory unless absolute |
RUST_LOG |
Unset | Logging filter, such as info or debug |
To configure a CLI context, edit ~/.starlight/cli/config.yaml (or the file under
CLI_DIR). For example:
current_context: local
contexts:
local:
name: local
description: Local Starlight server
endpoint: http://localhost:8246
api_key: ""List and select configured contexts with:
sl config get-contexts
sl config set-context --name localIf the server uses a different port or BASE_PATH, include it in the context's
endpoint, for example http://localhost:9000/api.
Starlight is a Cargo workspace with an API server and four crates:
src/ Axum API server and custom simulator task
crates/eng/ Workflow engine, task lifecycle, channels, and built-in tasks
crates/cli/ Command-line client and interactive generation TUI
crates/simulator/ Statistical signal models
crates/jb/ JSON builder utility
Build and run tests across the workspace:
cargo build --workspace
cargo test --workspace
cargo test -p engCustom tasks implement the Task trait and expose a
factory that accepts a task ID and JSON parameters. Register the factory with
engine.register_task("my_type", MyTask::create) before loading workflows. Use
ctx.running().await as the execution loop guard so tasks respond to pause and
stop commands. See the simulator task for an example.
Starlight is source-available under the Elastic License 2.0 (ELv2). The license allows use, modification, and redistribution subject to its terms. It prohibits providing Starlight to third parties as a hosted or managed service where users have access to any substantial set of its features or functionality. See LICENSE for the full terms.