Skip to content

Topics

Topics organize frames in the event stream. The . character creates hierarchy, enabling wildcard queries.

Naming Rules

RuleDetail
Allowed charactersa-z A-Z 0-9 _ - .
Must start witha-z A-Z _
Cannot end with.
Cannot contain..
Max length255 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.messages
user.alice.status
user.bob.messages

This structure enables prefix queries.

Wildcard Queries

Use .* suffix to query all direct and nested children of a prefix:

Terminal window
# All frames under user.alice (messages, status, etc.)
xs cat ./store --topic "user.alice.*"
# All user frames
xs 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.

Terminal window
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 lifecycle
xs.service.<name>.<event> service lifecycle
xs.action.<name>.<event> action lifecycle
xs.module.<name> module registration
xs.threshold, xs.stopping system markers

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

Terminal window
# Register a module
r#'export def greet [name: string] { $"hello ($name)" }'# | .append xs.module.mymod

The module name (after xs.module.) maps to a directory path, dots become slashes:

TopicVFS Path
xs.module.mymodmymod/mod.nu
xs.module.discord.apidiscord/api/mod.nu

Scripts reference modules by their stable VFS path. Re-appending a module topic shadows the previous version:

Terminal window
r#'{
run: {|frame, state|
use mymod
{out: {greeting: (mymod greet "world")}, next: $state}
}
}'# | .append xs.actor.greeter.create

Each 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

TopicDescription
xs.startSystem initialization complete
xs.thresholdStream processing threshold marker
xs.pulseSynthetic pulse events (configurable interval)
xs.stoppingServer-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.

Terminal window
{
run: {|| "Hello, World!" },
return_options: {
suffix: ".message" # Changes .recv to .message
}
}