Services
cross.stream services use Nushell closures to create streams of data
that are emitted as frames into the store.
You can also compose external request-response servers, particularly CLI tools that read requests on stdin and write responses to stdout, using duplex services.
Basic Usage
To create a service, append a Nushell script that evaluates to a configuration
record with a run closure using the topic <topic>.spawn:
r#'{ run: {|| ^tail -F http.log | lines }}'# | .append log.spawnThe service will:
- Execute the provided Nushell expression
- Output from the pipeline is streamed as
log.recvframes. Text pipelines emit one frame per line, whileByteStreampipelines send binary chunks. - Automatically restarts if it exits until a terminate frame is seen
Lifecycle Events
Services emit lifecycle events to track their state. See for all processor suffixes.
| Event | Description |
|---|---|
<topic>.running | Service has started processing |
<topic>.recv | Output value from the service |
<topic>.stopped | Service pipeline has stopped. The `meta.reason` field is a string enum with values finished, error, terminate and update. When finished or error, the pipeline will be restarted automatically; terminate means it was stopped manually and the service loop will shut down. update indicates the service reloaded due to a new .spawn frame. |
<topic>.parse.error | Script failed to parse |
<topic>.shutdown | Service loop has fully exited; the dispatcher evicts it |
All events include source_id which is the ID of the service instance. When a .stopped frame has meta.reason set to update, it also includes update_id referencing the spawn that triggered the reload. The dispatcher evicts a service when it receives a <topic>.shutdown frame.
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
duplex | boolean | false | Enable sending input to the service’s pipeline via <topic>.send |
return_options | record | — | Customize output frames (see Return Options) |
The return_options field controls the suffix and TTL for the .recv frames produced by the service.
Return Options
The return_options record allows you to customize the output frames produced by the service:
| Option | Type | Default | Description |
|---|---|---|---|
suffix | string | ”.recv” | Custom suffix for output frames (e.g., “.output”) |
ttl | string | ”forever” | Time-to-live for output frames |
TTL Options
"forever"- Frames never expire (default)"ephemeral"- Frames are removed immediately after processing"time:<ms>"- Frames expire after specified milliseconds"last:<n>"- Keep only the N most recent frames for each topic
Example with custom return options:
r#'{ run: {|| ^tail -F access.log | lines }, return_options: { suffix: ".line", ttl: "last:100" # Keep only last 100 log lines }}'# | .append logs.spawnThis service will emit frames with the topic logs.line and automatically maintain only the 100 most recent log entries.
Modules
Services can use modules registered via *.nu topics. A service sees the modules as they existed when it was spawned. See for details.
r#'{ run: {|| use xs/my-parser ^tail -F data.log | lines | each {|line| my-parser parse $line } }}'# | .append log.spawnBi-directional Communication
When duplex is enabled, you can send data into the service’s input pipeline
via <topic>.send frames:
# Create a websocket connectionr#'{ run: {|| websocat wss://echo.websocket.org | lines }, duplex: true}'# | .append echo.spawn
# Send input to the websocket: note the "\n", wss://echo.websocket.org won't# reply until it sees a complete line"hello\n" | .append echo.sendWhen running this service:
- Lines received from the websocket server are emitted as
<topic>.recvframes - Content from
<topic>.sendframes is sent to the websocket server
Error Handling
If a service encounters an error during spawning a <topic>.parse.error frame
is emitted with:
source_id: ID of the failed spawn attemptreason: Error message describing what went wrong
The service does not start and no stop frame is produced.
When a running service finishes or fails, it automatically restarts after a 1-second delay.
Stopping Services
To stop a running service, append a frame with the topic <topic>.terminate.
The service will stop and emit a <topic>.stopped frame with meta.reason set to
terminate.
Appending a new <topic>.spawn frame while a service of the same topic
is running reloads it with the new script. If the reload fails to parse,
you’ll see a <topic>.parse.error frame, and the previous service continues
running.