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).
Topic queries return frames in chronological order (by frame ID), not grouped by topic. Both historical reads and live follows maintain this ordering.
Module Topics
Topics ending with .nu 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 mymod.nuThe topic name (minus the .nu suffix) maps to a directory path — dots become slashes:
| Topic | VFS Path |
|---|---|
mymod.nu | mymod/mod.nu |
discord.api.nu | 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 greeter.registerEach actor, service, or command sees the modules as they existed when it was registered. Modules appended later are only visible to processors registered after them. Re-appending a module and then re-registering a processor picks up the new version.
Processor Suffixes
cross.stream uses topic suffixes to coordinate processor lifecycle.
Services
| Suffix | Description | Emitted By |
|---|---|---|
.spawn | Create or update a service | User |
.terminate | Stop a service | User |
.send | Input to duplex services | User |
.running | Service has started | System |
.stopped | Service pipeline stopped | System |
.shutdown | Service loop fully exited | System |
.parse.error | Script parsing failed | System |
.recv | Output from service | System |
Actors
| Suffix | Description | Emitted By |
|---|---|---|
.register | Register an actor | User |
.unregister | Unregister an actor | User |
.active | Actor is now active | System |
.unregistered | Actor has been removed | System |
.out | Output from actor | System |
Actions
| Suffix | Description | Emitted By |
|---|---|---|
.define | Define an action | User |
.call | Invoke an action | User |
.ready | Action ready for calls | System |
.error | Action execution failed | System |
.response | Action execution result | System |
System Topics
| Topic | Description |
|---|---|
xs.start | System initialization complete |
xs.threshold | Stream processing threshold marker |
xs.pulse | Synthetic pulse events (configurable interval) |
Customizable Suffixes
Output suffixes can be changed via return_options.suffix:
.recv(Services).out(Actors).response(Actions)
{ run: {|| "Hello, World!" }, return_options: { suffix: ".message" # Changes .recv to .message }}Event Metadata
Lifecycle and output events include:
source_id/actor_id/action_id: Component instance IDframe_id: Triggering frame ID (actors/actions)error: Error details (on error events)reason: Stop reason (on stopped events)