https://github.com/pancsta/asyncmachine-go
Batteries-included graph control flow library (AOP, actor model, state-machine)
https://github.com/pancsta/asyncmachine-go
actor-model aop concurrency consensus control-flow declarative distributed golang graph libp2p negotiation network orchestrator pubsub rpc state-machine sync workflows
Last synced: about 1 month ago
JSON representation
Batteries-included graph control flow library (AOP, actor model, state-machine)
- Host: GitHub
- URL: https://github.com/pancsta/asyncmachine-go
- Owner: pancsta
- License: mit
- Created: 2024-01-10T15:34:09.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2025-12-22T16:31:04.000Z (2 months ago)
- Last Synced: 2025-12-24T01:36:38.855Z (2 months ago)
- Topics: actor-model, aop, concurrency, consensus, control-flow, declarative, distributed, golang, graph, libp2p, negotiation, network, orchestrator, pubsub, rpc, state-machine, sync, workflows
- Language: Go
- Homepage: https://asyncmachine.dev
- Size: 22.1 MB
- Stars: 157
- Watchers: 1
- Forks: 3
- Open Issues: 30
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Roadmap: ROADMAP.md
Awesome Lists containing this project
README
[](https://goreportcard.com/report/github.com/pancsta/asyncmachine-go)
[](https://pkg.go.dev/github.com/pancsta/asyncmachine-go)
[](https://asyncmachine.dev)





[](https://github.com/pancsta/asyncmachine-go/commits/main/)
[](https://matrix.to/#/#room:asyncmachine)
#
asyncmachine-go
> [!NOTE]
> State machines communicate through states.
**asyncmachine-go** is a batteries-included graph control flow library implementing [AOP](https://en.wikipedia.org/wiki/Aspect-oriented_programming)
and [Actor Model](https://en.wikipedia.org/wiki/Actor_model) through a **[clock-based state-machine](/pkg/machine/README.md)**.
It features [atomic transitions](/docs/manual.md#transition-lifecycle), [relations with consensus](/docs/manual.md#relations),
[transparent RPC](/pkg/rpc/README.md), [TUI debugger](/tools/cmd/am-dbg/README.md),
[telemetry](/pkg/telemetry/README.md), [REPL](/tools/cmd/arpc/README.md), [selective distribution](/pkg/rpc/README.md#selective-distribution),
[remote workers](/pkg/node/README.md), and [diagrams](/tools/cmd/am-vis).
As a control flow library, it decides about running of predefined bits of code (transition handlers) - their order and
which ones to run, according to currently active states (flags). Thanks to a [novel state machine](/pkg/machine/README.md),
the number of handlers can be minimized while maximizing scenario coverage. It's lightweight, fault-tolerant by design,
has rule-based mutations, and can be used to target virtually *any* step-in-time, in *any* workflow. It's a low-level
tool with acceptable performance.
**asyncmachine-go** takes care of `context`, `select`, and `panic`, while allowing for graph-structured concurrency
with [goroutine cancelation](https://github.com/pancsta/asyncmachine-go/pull/261). The history log and relations have
vector formats. It aims to create **autonomous** workflows with **organic** control flow and **stateful** APIs.
#### *Each state represents*
- binary flag
- node with relations
- AOP aspect
- ***logical clock***
- subscription topic
- multiple methods
- metric
- trace
- lock
- breakpoint
Besides the main use-case of **workflows**, it can be used for **stateful applications of any size** - daemons, UIs,
configs, bots, firewalls, synchronization consensus, games, smart graphs, microservice orchestration, robots, contracts,
streams, DI containers, test scenarios, simulators, as well as **"real-time" systems** which rely on instant
cancelation.
> [!NOTE]
> Flow is state, and state is flow, in a graph.
## Samples
**Minimal** - an untyped definition of 2 states and 1 relation, then 1 mutation and a check.
```go
import am "github.com/pancsta/asyncmachine-go/pkg/machine"
// ...
mach := am.New(nil, am.Schema{
"Foo": {Require: am.S{"Bar"}},
"Bar": {},
}, nil)
mach.Add1("Foo", nil)
mach.Is1("Foo") // false
```
**Complicated** - wait on a multi state (event) and the Ready state with a 1s
timeout, then mutate with typed args, on top of a state context.
```go
// state ctx is an expiration ctx
ctx := client.Mach.NewStateCtx(ssC.WorkerReady)
// clock-based subscription
whenPayload := client.Mach.WhenTicks(ssC.WorkerPayload, 1, ctx)
// state mutation
client.RpcWorker.NetMach.Add1(ssW.WorkRequested, Pass(&A{
Input: 2}))
// WaitFor* wraps select statements
err := amhelp.WaitForAll(ctx, time.Second,
// post-mutation subscription
mach.When1(ss.BasicStatesDef.Ready, nil),
// pre-mutation subscription
whenPayload)
// check cancelation
if ctx.Err() != nil {
return // state ctx expired
}
// check error
if err != nil {
// error state mutation
client.Mach.AddErr(err, nil)
return // no err required
}
// client/WorkerPayload and mach/Ready activated
```
> [!NOTE]
> Clock-based navigation in time.
**Handlers** - [Aspect Oriented](https://en.wikipedia.org/wiki/Aspect-oriented_programming) transition handlers.
```go
// can Foo activate?
func (h *Handlers) FooEnter(e *am.Event) bool {
return true
}
// with Foo active, can Bar activate?
func (h *Handlers) FooBar(e *am.Event) bool {
return true
}
// Foo activates
func (h *Handlers) FooState(e *am.Event) {
h.foo = NewConn()
}
// Foo de-activates
func (h *Handlers) FooEnd(e *am.Event) {
h.foo.Close()
}
```
**Schema** - states of a [node worker](/pkg/node/README.md).
```go
type WorkerStatesDef struct {
ErrWork string
ErrWorkTimeout string
ErrClient string
ErrSupervisor string
LocalRpcReady string
PublicRpcReady string
RpcReady string
SuperConnected string
ServeClient string
ClientConnected string
ClientSendPayload string
SuperSendPayload string
Idle string
WorkRequested string
Working string
WorkReady string
// inherit from rpc worker
*ssrpc.NetMachStatesDef
}
```
All examples and benchmarks can be found in [`/examples`](/examples/README.md).
## Getting Started
- 🦾 **[`/pkg/machine`](pkg/machine/README.md)** is the main package
- [`/docs/diagrams.md`](/docs/diagrams.md) try to explain things visually
- [`/pkg/node`](pkg/node) shows a high-level usage
- examples in [`/examples`](/examples/README.md) are good for a general grasp
- with [`/examples/mach_template`](/examples/mach_template) being ready for copy-paste
- [`/docs/manual.md`](/docs/manual.md) is the go-to
- [`/tools/cmd/am-gen`](/tools/cmd/am-gen) will bootstrap
- [`/tools/cmd/am-dbg`](/tools/cmd/am-dbg) will record every detail
- and [reading tests](https://github.com/search?q=repo%3Apancsta%2Fasyncmachine-go+path%3A%2F.*_test.go%2F&type=code)
is always a good idea
## [Packages](/pkg)
This monorepo offers the following importable packages, especially:
- 🦾 **[`/pkg/machine`](/pkg/machine) State machine, dependency free, semver compatible.**
- [`/pkg/states`](/pkg/states) Reusable state schemas, handlers, and piping.
- [`/pkg/helpers`](/pkg/helpers) Useful functions when working with async state machines.
- [`/pkg/telemetry`](/pkg/telemetry) Telemetry exporters for dbg, metrics, traces, and logs.
Other packages:
- [`/pkg/rpc`](/pkg/rpc) Remote state machines, with the same API as local ones.
- [`/pkg/history`](/pkg/history) History tracking and traversal, including Key-Value and SQL.
- [`/pkg/integrations`](/pkg/integrations) Integrations with NATS and JSON.
- [`/pkg/graph`](/pkg/graph) Directional multigraph of connected state machines.
- [`/pkg/node`](/pkg/node) Distributed worker pools with supervisors.
- [`/pkg/pubsub`](/pkg/pubsub) Decentralized PubSub based on libp2p gossipsub.
## [Devtools](/tools)
- [`/tools/cmd/am-dbg`](/tools/cmd/am-dbg) Multi-client TUI debugger.
- [`/tools/cmd/am-gen`](/tools/cmd/am-gen) Generates schema files and Grafana dashboards.
- [`/tools/cmd/arpc`](/tools/cmd/arpc) Network-native REPL and CLI.
- [`/tools/cmd/am-vis`](/tools/cmd/am-vis) Generates D2 diagrams.
- [`/tools/cmd/am-relay`](/tools/cmd/am-relay) Rotates logs.
> [!NOTE]
> Inspecting cause-and-effect in distributed systems.
## Apps
- [secai](https://github.com/pancsta/secai) AI Agents framework.
- [arpc REPL](/tools/repl) Cobra-based REPL.
- [am-dbg TUI Debugger](/tools/debugger) Single state-machine TUI app.
- [libp2p PubSub Simulator](https://github.com/pancsta/go-libp2p-pubsub-benchmark/#libp2p-pubsub-simulator) Sandbox
simulator for libp2p-pubsub.
- [libp2p PubSub Benchmark](https://github.com/pancsta/go-libp2p-pubsub-benchmark/#libp2p-pubsub-benchmark)
Benchmark of libp2p-pubsub ported to asyncmachine-go.
## Documentation
- API: [go.dev](https://pkg.go.dev/github.com/pancsta/asyncmachine-go/pkg/machine) / [code.asyncmachine.dev](https://code.asyncmachine.dev)
- [diagrams](/docs/diagrams.md) / [cookbook](/docs/cookbook.md)
- [manual MD](/docs/manual.md) / [manual PDF](https://pancsta.github.io/assets/asyncmachine-go/manual.pdf)
- [Machine and States](/docs/manual.md#machine-and-states)
- [Changing State](/docs/manual.md#changing-state)
- [Advanced Topics](/docs/manual.md#advanced-topics)
- [Cheatsheet](/docs/manual.md#cheatsheet)
## Goals
- scale up, not down
- defaults work by default
- everything can be traced and debugged
- automation is evolution
- state != data
## Community
- [GitHub discussions](https://github.com/pancsta/asyncmachine-go/discussions)
- [Matrix chat](https://matrix.to/#/#room:asyncmachine)
- [Author's Feed](https://blogic.tech/feed)
> [!NOTE]
> Hundreds of clones.
## Status
Under development, status depends on each package. The bottom layers seem prod grade, the top ones are alpha or testing.
> [!NOTE]
> Managing distributed concurrency.
## Development
- [good first issues](https://github.com/pancsta/asyncmachine-go/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22)
- before
- `./scripts/dep-taskfile.sh`
- `task install-deps`
- after
- `task test`
- `task format`
- `task lint`
- `task precommit`
### Roadmap
- more tooling
- bug fixes and optimizations
- [ROADMAP.md](/ROADMAP.md)
## [FAQ](/FAQ.md)
### How does asyncmachine work?
It calls struct methods according to conventions, a schema, and currently active states (eg `BarEnter`, `FooFoo`,
`FooBar`, `BarState`). It tackles nondeterminism by embracing it - like an UDP event stream with structure.
### What is a "state" in asyncmachine?
State is a binary ID as in status / switch / flag, eg "process RUNNING" or "car BROKEN".
### What does "clock-based" mean?
Each state has a counter of activations & deactivations, and all state counters create "machine time". These are logical
clocks, and the queue is also (partially) counted.
### What's the difference between states and events?
The same event happening many times will cause only 1 state activation, until the state becomes inactive.
The complete FAQ is available at [FAQ.md](/FAQ.md).
## Changes
- [Changelog](/CHANGELOG.md)
- [Breaking Changes](/BREAKING.md)
- [Repo Traffic](https://github.com/pancsta/asyncmachine-go/pulse)
- [Release Feed](https://github.com/pancsta/asyncmachine-go/releases.atom)
> [!NOTE]
> Don't lose your sync.
