Introduction
Maestro is an embedded workflow orchestration runtime originally designed for KYC and onboarding systems.
It helps backend applications execute long-running flows involving:
- human approval steps
- external vendor actions
- pause/resume execution
- persistence + restore
- workflow-driven orchestration
Unlike orchestration platforms that require separate infrastructure, Maestro runs directly inside your Go application as a library.
Typical use cases:
- KYC onboarding
- manual review systems
- fintech operations
- multi-step onboarding
- backoffice workflows
- approval flows
Quick Start
Library (embedding)
go get github.com/justinush/maestro@v0.1.1
Pin a release tag in production; @latest works for trying the module locally.
CLI
go install github.com/justinush/maestro/cmd/maestro@v0.1.1
maestro --version
Or download a binary from GitHub Releases — e.g. maestro_v0.1.1_darwin_arm64.tar.gz on macOS, or .zip on Windows. Check SHA256SUMS on the release page when verifying downloads.
Minimal example
package main
import (
"fmt"
"github.com/justinush/maestro/pkg/engine"
"github.com/justinush/maestro/pkg/maestro"
)
func main() {
rt, err := maestro.Load("workflow.yaml")
if err != nil {
panic(err)
}
in, err := rt.NewInstance(maestro.InstanceOptions{})
if err != nil {
panic(err)
}
res := in.RunUntilBlocked()
switch res.Status {
case engine.RunBlocked:
fmt.Println("workflow paused for human input")
case engine.RunCompleted:
fmt.Println("workflow completed")
case engine.RunFailed:
panic(res.Err)
}
}
Example workflow:
schemaVersion: "0.1"
id: onboarding
version: "1.0.0"
initialStepId: collect-profile
terminalStepIds:
- approved
steps:
- id: collect-profile
kind: human
- id: approved
kind: end
transitions:
- from: collect-profile
to: approved
Embedding (canonical path)
Use pkg/maestro as the main embedding API. Lower-level packages (pkg/engine, pkg/run) are for advanced customization.
maestro.Load*
-> Runtime.NewInstance
-> RunUntilBlocked
-> SubmitInput (when blocked)
Persistence across HTTP requests:
run.RecordFromInstance -> Store.Create / Store.Save
Store.Get -> rt.RestoreInstance (preferred)
Example restore (same workflow definition as when the run started):
rec, err := runs.Get(ctx, runID)
if err != nil {
return err
}
in, err := rt.RestoreInstance(rec, maestro.InstanceOptions{
ActionRegistry: reg, // same registry semantics as NewInstance
})
For custom Store implementations, run.InstanceFromRecord is the lower-level equivalent of RestoreInstance.
Provide your own *http.Client with engine.RegistryWithHTTP(client) for HTTP actions in production; the CLI simulate command uses an internal long-timeout client and does not export it from pkg/engine.
Architecture
Maestro follows an embedded orchestration model.
Your application owns:
- HTTP APIs
- authentication
- business data
- UI
- database models
Maestro owns:
- workflow graph execution
- orchestration lifecycle
- transitions
- human steps
- execution trace

Examples
The repository includes progressively more advanced examples.
Core Concepts
Runtime
A loaded and validated workflow definition.
rt, err := maestro.Load("workflow.yaml")
Instance
A running workflow execution.
in, err := rt.NewInstance(maestro.InstanceOptions{})
Human Steps
Execution pauses until the application submits input.
sub := in.SubmitInput(...)
Persistence
Workflow runs are stored separately from your business data.
rec := run.RecordFromInstance(in, def, revision) // revision 0 before Create
err = store.Create(ctx, rec) // stored revision becomes 1
rec, err = store.Get(ctx, runID) // read current revision
// ... mutate via SubmitInput / RunUntilBlocked ...
err = store.Save(ctx, rec) // requires matching revision
Restore on a later request (canonical path):
in, err := rt.RestoreInstance(rec, maestro.InstanceOptions{})
Store.Save returns run.ErrRevisionConflict when another request updated the run first (optimistic locking).
See architecture notes for details.
Postgres store
For durable backends, use the official Postgres adapter in pkg/run/postgres:
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/justinush/maestro/pkg/run/postgres"
)
pool, err := pgxpool.New(ctx, databaseURL)
if err != nil {
return err
}
// Ensure workflow_runs exists (see schema note below).
store := postgres.NewStore(pool) // implements run.Store
Schema: pkg/run/postgres/schema.sql defines workflow_runs (intended stable across v0.x). Either:
postgres.ApplySchema — optional, idempotent helper for examples, tests, and local dev.
- Your migration tool — copy
schema.sql or use postgres.SchemaDDL() in goose, golang-migrate, Atlas, etc.
Production apps usually prefer the second option. run.NewMemoryStore remains for in-process tests and demos only.
Actions
Workflow steps can execute external logic through registered runners.
Embedders supply their own HTTP client:
reg := engine.RegistryWithHTTP(client)
Repository Structure
| Path |
Purpose |
pkg/maestro |
canonical embedding API (start here) |
pkg/engine |
workflow runtime (advanced / custom registries) |
pkg/run |
persistence types and Store |
pkg/run/postgres |
Postgres Store adapter (JSONB, optimistic locking) |
pkg/definition |
workflow schema + decoding |
pkg/validate |
workflow validation |
examples/demos |
focused learning examples |
examples/apps |
near real-world applications |
Project Status
Latest release: v0.1.1 (CLI version reporting and cross-platform binaries). Maestro is still early v0.x — APIs may evolve before v1.
See CHANGELOG.md for release notes and docs/roadmap.md for what's next.
Stability for v0.x: JSON field names on run.RunRecord, engine.Snapshot, and engine.Event, plus RunStatus / SubmitInputStatus values, are treated as stable. Breaking changes to those shapes will require a major version bump.
Changelog
Release notes: CHANGELOG.md.
Roadmap
Direction and priorities: docs/roadmap.md.
Roughly next: workflow registries, versioning, async callbacks, retries/timers, observability.
Why Maestro?
Maestro focuses on:
- embedded-first architecture
- readable workflows
- human + system orchestration
- backend-oriented integration
- application-owned control
- simple runtime embedding
The goal is to make workflow orchestration feel natural inside normal backend services.
Contributing
Contributions, feedback, and discussions are welcome.
Helpful links:
License
MIT