Skip to content

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:

Terminal window
r#'{
run: {|| ^tail -F http.log | lines }
}'# | .append log.spawn

The service will:

  • Execute the provided Nushell expression
  • Output from the pipeline is streamed as log.recv frames. Text pipelines emit one frame per line, while ByteStream pipelines 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.

EventDescription
<topic>.runningService has started processing
<topic>.recvOutput value from the service
<topic>.stoppedService 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.errorScript failed to parse
<topic>.shutdownService 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

OptionTypeDefaultDescription
duplexbooleanfalseEnable sending input to the service’s pipeline via <topic>.send
return_optionsrecordCustomize 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:

OptionTypeDefaultDescription
suffixstring”.recv”Custom suffix for output frames (e.g., “.output”)
ttlstring”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:

Terminal window
r#'{
run: {|| ^tail -F access.log | lines },
return_options: {
suffix: ".line",
ttl: "last:100" # Keep only last 100 log lines
}
}'# | .append logs.spawn

This 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.

Terminal window
r#'{
run: {||
use xs/my-parser
^tail -F data.log | lines | each {|line| my-parser parse $line }
}
}'# | .append log.spawn

Bi-directional Communication

When duplex is enabled, you can send data into the service’s input pipeline via <topic>.send frames:

Terminal window
# Create a websocket connection
r#'{
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.send

When running this service:

  • Lines received from the websocket server are emitted as <topic>.recv frames
  • Content from <topic>.send frames 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 attempt
  • reason: 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.