2026-03-11 13:02:37 +00:00
---
title: Messaging
2026-03-25 17:13:27 +00:00
description: ACTION, QUERY, QUERYALL, named Actions, and async dispatch.
2026-03-11 13:02:37 +00:00
---
# Messaging
2026-03-25 17:13:27 +00:00
CoreGO has two messaging layers: anonymous broadcast (ACTION/QUERY) and named Actions.
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
## Anonymous Broadcast
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
### `ACTION`
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
Fire-and-forget broadcast to all registered handlers. Each handler is wrapped in panic recovery. Handler return values are ignored — all handlers fire regardless.
2026-03-11 13:02:37 +00:00
```go
2026-03-21 10:05:04 +00:00
c.RegisterAction(func(_ *core.Core, msg core.Message) core.Result {
2026-03-25 17:13:27 +00:00
if ev, ok := msg.(repositoryIndexed); ok {
core.Info("indexed", "name", ev.Name)
}
return core.Result{OK: true}
2026-03-11 13:02:37 +00:00
})
2026-03-25 17:13:27 +00:00
c.ACTION(repositoryIndexed{Name: "core-go"})
2026-03-11 13:02:37 +00:00
```
2026-03-25 17:13:27 +00:00
### `QUERY`
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
First handler to return `OK:true` wins.
2026-03-11 13:02:37 +00:00
```go
2026-03-21 10:05:04 +00:00
c.RegisterQuery(func(_ *core.Core, q core.Query) core.Result {
2026-03-25 17:13:27 +00:00
if _, ok := q.(repositoryCountQuery); ok {
return core.Result{Value: 42, OK: true}
}
return core.Result{}
2026-03-11 13:02:37 +00:00
})
2026-03-21 10:05:04 +00:00
r := c.QUERY(repositoryCountQuery{})
2026-03-11 13:02:37 +00:00
```
2026-03-25 17:13:27 +00:00
### `QUERYALL`
2026-03-21 10:05:04 +00:00
2026-03-25 17:13:27 +00:00
Collects every successful non-nil response.
2026-03-11 13:02:37 +00:00
```go
2026-03-21 10:05:04 +00:00
r := c.QUERYALL(repositoryCountQuery{})
results := r.Value.([]any)
2026-03-11 13:02:37 +00:00
```
2026-03-25 17:13:27 +00:00
## Named Actions
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
Named Actions are the typed, inspectable replacement for anonymous dispatch. See Section 18 of `RFC.md` .
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
### Register and Invoke
2026-03-11 13:02:37 +00:00
```go
2026-03-25 17:13:27 +00:00
// Register during OnStartup
c.Action("repo.sync", func(ctx context.Context, opts core.Options) core.Result {
name := opts.String("name")
return core.Result{Value: "synced " + name, OK: true}
2026-03-11 13:02:37 +00:00
})
2026-03-21 10:05:04 +00:00
2026-03-25 17:13:27 +00:00
// Invoke by name
r := c.Action("repo.sync").Run(ctx, core.NewOptions(
core.Option{Key: "name", Value: "core-go"},
))
2026-03-11 13:02:37 +00:00
```
2026-03-25 17:13:27 +00:00
### Capability Check
2026-03-11 13:02:37 +00:00
```go
2026-03-25 17:13:27 +00:00
if c.Action("process.run").Exists() {
// go-process is registered
}
c.Actions() // []string of all registered action names
2026-03-11 13:02:37 +00:00
```
2026-03-25 17:13:27 +00:00
### Permission Gate
2026-03-21 10:05:04 +00:00
2026-03-25 17:13:27 +00:00
Every `Action.Run()` checks `c.Entitled(action.Name)` before executing. See Section 21 of `RFC.md` .
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
## Task Composition
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
A Task is a named sequence of Actions:
2026-03-11 13:02:37 +00:00
```go
2026-03-25 17:13:27 +00:00
c.Task("deploy", core.Task{
Steps: []core.Step{
{Action: "go.build"},
{Action: "go.test"},
{Action: "docker.push"},
{Action: "notify.slack", Async: true},
},
2026-03-11 13:02:37 +00:00
})
2026-03-25 17:13:27 +00:00
r := c.Task("deploy").Run(ctx, c, opts)
2026-03-11 13:02:37 +00:00
```
2026-03-25 17:13:27 +00:00
Sequential steps stop on first failure. `Async: true` steps fire without blocking. `Input: "previous"` pipes output.
## Background Execution
2026-03-11 13:02:37 +00:00
```go
2026-03-25 17:13:27 +00:00
r := c.PerformAsync("repo.sync", opts)
taskID := r.Value.(string)
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
c.Progress(taskID, 0.5, "indexing commits", "repo.sync")
```
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
Broadcasts `ActionTaskStarted` , `ActionTaskProgress` , `ActionTaskCompleted` as ACTION messages.
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
### Completion Listener
2026-03-11 13:02:37 +00:00
```go
2026-03-25 17:13:27 +00:00
c.RegisterAction(func(_ *core.Core, msg core.Message) core.Result {
if ev, ok := msg.(core.ActionTaskCompleted); ok {
core.Info("done", "task", ev.TaskIdentifier, "ok", ev.Result.OK)
}
return core.Result{OK: true}
})
2026-03-11 13:02:37 +00:00
```
2026-03-25 17:13:27 +00:00
## Shutdown
2026-03-11 13:02:37 +00:00
2026-03-25 17:13:27 +00:00
When shutdown has started, `PerformAsync` returns an empty `Result` . `ServiceShutdown` drains outstanding background work before stopping services.