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: 2 months 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 (over 2 years ago)
- Default Branch: main
- Last Pushed: 2025-12-22T16:31:04.000Z (6 months ago)
- Last Synced: 2025-12-24T01:36:38.855Z (6 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 distributed workflow engine (technically a pathless control-flow graph with a consensus),
which implements [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](/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),
[diagrams](/tools/cmd/am-vis/README.md), and [WASM](/docs/wasm.md) support.
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 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, message broking, 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()
}
```
**Schemas** - relational schemas ([aRPC server](/pkg/rpc/states/ss_rpc.go)).
```go
var ServerSchema = am.Schema{
ssF.ClientConnected: {Require: S{ssS.RpcReady}},
ssF.ErrDelivery: {Require: S{ssS.Exception}},
ssF.ErrHandlerTimeout: {
Add: S{ssS.Exception},
Multi: true,
Require: S{ssS.Exception},
},
ssF.ErrNetwork: {
Remove: S{ssS.ClientConnected},
Require: S{ssS.Exception},
},
ssF.ErrNetworkTimeout: {Require: S{ssS.Exception}},
ssF.ErrOnClient: {Require: S{ssS.Exception}},
ssF.ErrProviding: {Require: S{ssS.Exception}},
ssF.ErrRpc: {Require: S{ssS.Exception}},
ssF.ErrSendPayload: {Require: S{ssS.Exception}},
ssF.Exception: {Multi: true},
ssF.HandshakeDone: {
Remove: S{ssS.Handshaking, ssS.HandshakeDone, ssS.Exception},
Require: S{ssS.Start, ssS.ClientConnected},
},
ssF.Handshaking: {
Remove: S{ssS.Handshaking, ssS.HandshakeDone},
Require: S{ssS.Start},
},
ssF.Healthcheck: {Multi: true},
ssF.Heartbeat: {},
ssF.MetricSync: {Multi: true},
ssF.Ready: {
Auto: true,
Require: S{ssS.HandshakeDone, ssS.RpcReady},
},
ssF.RpcAccepting: {
Remove: S{ssS.RpcStarting, ssS.RpcAccepting, ssS.RpcReady},
Require: S{ssS.Start},
},
ssF.RpcReady: {
Remove: S{ssS.RpcStarting, ssS.RpcAccepting, ssS.RpcReady},
Require: S{ssS.Start},
},
ssF.RpcStarting: {
Remove: S{ssS.RpcStarting, ssS.RpcAccepting, ssS.RpcReady},
Require: S{ssS.Start},
},
ssF.SendPayload: {Multi: true},
ssF.Start: {Add: S{ssS.RpcStarting}},
ssF.WebSocketTunnel: {},
}
```
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/manual.md`](/docs/manual.md) is the go-to
- [`/docs/diagrams.md`](/docs/diagrams.md) try to explain things visually
- [`/examples`](/examples/README.md) show use cases and integrations
- with [`/examples/mach_template`](/examples/mach_template) being ready for copy-paste
- also [DAG](/examples/dag_dependency_graph), [CLI Daemon](/examples/cli_daemon), [aRPC](/examples/arpc), [WASM](/examples/wasm),
[WASM Workflow](/examples/wasm_workflow), [TUI](/examples/tui)
- [`/tools/cmd/am-gen`](/tools/cmd/am-gen) will bootstrap
- [`/tools/cmd/am-dbg`](/tools/cmd/am-dbg/README.md) will record every detail
- [`/pkg/node`](pkg/node) shows a high-level usage
- 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/README.md) 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 and relays WASM.
> [!NOTE]
> Inspecting cause-and-effect in distributed systems.
## Apps
**asyncmachine-go** synchronizes state for the following projects:
- [secai](https://github.com/pancsta/secai) - AI Workflows framework
- [secai Web UI](https://github.com/pancsta/secai/tree/main/web) - WebAssembly [go-app](https://go-app.dev/) PWA
- Self-hosting of [pkg/rpc](pkg/rpc/states), [pkg/node](pkg/node/states), [pkg/pubsub](pkg/pubsub/states)
- [arpc REPL](/tools/repl/states) - Cobra-based REPL
- [am-dbg TUI Debugger](/tools/debugger/states) - 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: [pkg.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 RSS](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 and diagrams
- bug fixes, optimizations
- network security, ACLs
- [ROADMAP.md](/ROADMAP.md)
> [!NOTE]
> Step by step.
## [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.
