Topics
Topics organize frames in the event stream. The . character creates
hierarchy, enabling wildcard queries.
Naming Rules
| Rule | Detail |
|---|---|
| Allowed characters | a-z A-Z 0-9 _ - . |
| Must start with | a-z A-Z _ |
| Cannot end with | . |
| Cannot contain | .. |
| Max length | 255 bytes |
Valid: chat, user.alice, orders.2024.pending, my_topic-v2
Invalid: .hidden, -dash, 123, trailing., foo..bar, has space
Hierarchy
The . separator creates a topic hierarchy:
user.alice.messagesuser.alice.statususer.bob.messagesThis structure enables prefix queries.
Wildcard Queries
Use .* suffix to query all direct and nested children of a prefix:
# All frames under user.alice (messages, status, etc.)xs cat ./store --topic "user.alice.*"
# All user framesxs cat ./store --topic "user.*"The wildcard user.* matches user.alice, user.alice.messages, and
user.bob, but not user itself.
Use * alone to match all topics (equivalent to omitting --topic).
A pattern can be a comma-separated list; a frame matches if it matches any entry. Commas are a safe separator because topic names cannot contain them.
xs cat ./store --topic "game.move.*,game.create"Topic filtering is one of the read options,
accepted by every read surface; that page covers how a filter composes with
following and ordering. The Nushell --topic flag also accepts a list of
strings in place of a comma-separated string.
The xs. namespace
Topics that begin with xs. are owned by the runtime. Everything else is app
data:
xs.actor.<name>.<event> actor lifecyclexs.service.<name>.<event> service lifecyclexs.action.<name>.<event> action lifecyclexs.module.<name> module registrationxs.threshold, xs.stopping system markersA topic that starts with xs. is runtime-managed; everything else is app
data. See the lifecycle reference for the full lifecycle
vocabulary and the algorithm that consumes it.
Module Topics
Topics under xs.module.<name> register Nushell modules into the virtual
filesystem (VFS). Any actor, service, or action script can then use these
modules by their stable VFS path.
# Register a moduler#'export def greet [name: string] { $"hello ($name)" }'# | .append xs.module.mymodThe module name (after xs.module.) maps to a directory path, dots become
slashes:
| Topic | VFS Path |
|---|---|
xs.module.mymod | mymod/mod.nu |
xs.module.discord.api | discord/api/mod.nu |
Scripts reference modules by their stable VFS path. Re-appending a module topic shadows the previous version:
r#'{ run: {|frame, state| use mymod {out: {greeting: (mymod greet "world")}, next: $state} }}'# | .append xs.actor.greeter.createEach actor, service, or action sees the modules as they existed when it was created. Modules appended later are only visible to processors created after them. Re-appending a module and then re-creating a processor picks up the new version.
Because of this, retain a module version for at least as long as anything
relies on it. A pruning TTL (last:n or time:) can delete a version an
existing processor still resolves against and make its use fail with
ModuleNotFound; the simplest guarantee is to leave module topics on the
default forever. See TTL.
System Topics
| Topic | Description |
|---|---|
xs.start | System initialization complete |
xs.threshold | Stream processing threshold marker |
xs.pulse | Synthetic pulse events (configurable interval) |
xs.stopping | Server-wide shutdown signal |
Output suffix customization
Output suffixes can be changed via return_options.suffix:
.recv(services).out(actors).response(actions)
These are app-namespace data topics, not lifecycle topics; they stay outside
the xs. namespace.
{ run: {|| "Hello, World!" }, return_options: { suffix: ".message" # Changes .recv to .message }}