Service Lifecycle
This tutorial walks through every stage of a lifecycle so you can build intuition for what happens under the hood.
Prerequisites
- xs installed and on your PATH (see )
- Two terminal windows, both running
Nushellwithuse xs.nu *
Serve
Start a store in terminal 1:
xs serve ./store
Monitor the stream
In terminal 2, start a live monitor so we can watch lifecycle frames as they appear:
.cat -f | each { if $in.hash != null { insert content { .cas $in.hash } } else { } | print ($in | table -e)}Keep this running — every frame we discuss will show up here.
Spawn a service
Create a temporary file for the service to watch:
touch /tmp/log.txtNow spawn a service that tails the file:
r#'{ run: {|| ^tail -F /tmp/log.txt | lines }}'# | .append log.spawnYour monitor shows two frames:
.cat | where { $in.topic | str starts-with "log." }-#-+---topic-----+------------id-------------+-hash-+----------meta----------0 | log.spawn | 03abc0000000000000000000a | ... |1 | log.running | 03abc0000000000000000000b | | -------------+---------| | | | source_id | 03ab...| | | | -------------+----------#-+---topic-----+------------id-------------+-hash-+----------meta----------
log.spawn carries the script in CAS. log.running signals the pipeline is live.
See output
Write some lines to the file:
"hello\nworld\n" | save -a /tmp/log.txtTwo log.recv frames appear — one per line. Read the content back:
.last log.recv | .cas $in.hashworld
Each line the service produces is stored as a separate log.recv frame with its
content in CAS.
Auto-restart
Remove the file so tail exits:
rm /tmp/log.txtThe monitor shows the service stop and restart:
.cat | where topic == "log.stopped" | last | get meta-----------+--------------------------reason | finishedsource_id | 03ab...-----------+--------------------------
The reason is finished — the pipeline exited on its own. After a 1-second
pause, a new log.running frame appears and the service is alive again.
Recreate the file and write to it:
touch /tmp/log.txt"back in business\n" | save -a /tmp/log.txtA fresh log.recv frame appears. The service recovered automatically.
Hot reload
Append a new script to log.spawn while the service is running:
r#'{ run: {|| ^tail -F /tmp/log.txt | lines | each {|line| $"[LOG] ($line)" } }}'# | .append log.spawnThe running service picks up the new spawn, stops the old pipeline, and starts the new one:
.cat | where topic == "log.stopped" | last | get meta-----------+--------------------------reason | updatesource_id | 03ab...update_id | 03ab...-----------+--------------------------
The reason is update and update_id points to the new spawn frame. A
log.running frame follows immediately.
Verify the new behavior:
"reloaded\n" | save -a /tmp/log.txt.last log.recv | .cas $in.hash[LOG] reloaded
The output now includes the [LOG] prefix from the updated script.
Terminate
Stop the service explicitly:
.append log.terminateThe monitor shows three frames in sequence:
log.terminate— your requestlog.stoppedwithmeta.reasonset toterminatelog.shutdown— the service loop has fully exited
.cat | where topic == "log.stopped" | last | get meta-----------+--------------------------reason | terminatesource_id | 03ab...-----------+--------------------------
After log.shutdown, the dispatcher evicts the service. It will not restart.
Graceful shutdown
When xs itself stops (e.g. Ctrl+C), it emits an xs.stopping frame. Every
running service sees this frame, interrupts its pipeline, and emits the usual
.stopped / .shutdown sequence — just like a terminate, but with a distinct
reason.
Restart the service so we can see this in action:
r#'{ run: {|| ^tail -F /tmp/log.txt | lines }}'# | .append log.spawnWait for log.running to appear in the monitor, then press Ctrl+C in
terminal 1 (where xs serve is running).
The monitor shows:
xs.stopping— emitted by xs before exitlog.stoppedwithmeta.reasonset toshutdownlog.shutdown— the service loop has fully exited
.cat | where topic == "log.stopped" | last | get meta-----------+--------------------------reason | shutdownsource_id | 03ab...-----------+--------------------------
xs waits up to a few seconds for all services to finish before exiting. This gives services a chance to flush output and clean up resources.
Recap
| Suffix | Emitted when |
|---|---|
.spawn | You append a script to start (or reload) the service |
.running | The pipeline is live and producing output |
.recv | Each line or chunk of output from the pipeline |
.stopped | The pipeline exited — check meta.reason for why |
.shutdown | The service loop has fully exited (terminate or shutdown) |
Stopped reasons:
| Reason | What happened | What follows |
|---|---|---|
finished | Pipeline exited on its own | Auto-restart after 1s |
error | Pipeline failed | Auto-restart after 1s |
update | New .spawn frame arrived | Immediate restart |
terminate | .terminate frame arrived | .shutdown, no restart |
shutdown | xs.stopping frame arrived | .shutdown, no restart |