Skip to content
Zagforge-OrgPublic

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

maestro

maestro

CI

Maestro is a CLI tool that scaffolds scalable Go microservice projects. It follows an opinionated design pattern - go.work, per-service go.mod, Dockerfiles, docker-compose, hot reload, secrets, databases, and Taskfiles. Managing growing projects in Go can be tough, but with a single task up command you can get it up and running immediately.

Unlike a one-shot generator, maestro reconciles: project.toml is the source of truth, and maestro refresh brings every generated file back in line on demand - without clobbering your edits.

Built on top of the project.toml philosophy: one workspace, many services under it, each in its own module, all reconciled from a single project.toml

Scaffolding a Postgres-backed HTTP service and a worker while the go.work workspace stays in sync

What it does

  • Bootstraps a workspace with go.work, a shared common/go module with handy starter code, dev and prod compose files, Dockerfiles, Air hot-reload, .gitignore, and a root Taskfile.

  • Generates services in four shapes (bare, http, grpc, worker), each with its own cmd/, internals, Taskfile, and (optionally) sqlc/goose wiring for Postgres or SQLite — in Docker or against a live URL.

  • Refreshes the project state via refresh — change project.toml, run one command, and all generated files (compose, Taskfile, go.mod replace directives) catch up. User edits are preserved.

  • Stays out of your secrets. Doppler is the only built-in provider. Maestro writes doppler.yaml and reads via doppler secrets download. It never creates projects or pushes secrets.

Install

The quickest path is go install. Prebuilt binaries are on the releases page for machines without a Go toolchain.

From a release

Download a prebuilt binary straight from the releases page, or fetch it with gh:

Linux / macOS

gh release download --repo Zagforge-Org/maestro \
    --pattern '*Linux_x86_64.tar.gz'              # or *Darwin_arm64.tar.gz, etc.
tar xzf maestro_*.tar.gz
sudo mv maestro /usr/local/bin/
maestro version

Windows (PowerShell)

gh release download --repo Zagforge-Org/maestro --pattern '*Windows_x86_64.zip'
Expand-Archive maestro_*.zip -DestinationPath .
# Drop maestro.exe somewhere on PATH (create the dir + add to PATH if needed):
Move-Item maestro.exe "$Env:LOCALAPPDATA\Programs\maestro\maestro.exe"
maestro version

Available archives per release: Linux_x86_64, Linux_arm64, Darwin_x86_64, Darwin_arm64, Windows_x86_64, Windows_arm64.

From source

Requires a Go toolchain:

go install github.com/Zagforge-Org/maestro@latest

Quickstart

mkdir my-project && cd my-project
maestro init              # interactive: name, repo owner, optional Doppler project
maestro service api       # interactive: shape (bare/http/grpc/worker), optional DB
task api:dev                  # hot-reloaded local dev for that one service
task up                       # bring every service up via docker-compose

To check that all required tools are installed:

maestro doctor

Non-interactive mode

Both init and service launch a TUI wizard by default. Pass -y (or --yes) and the flag values become the source of truth - handy for CI, setup scripts, and reproducible playbooks.

# Scaffold a project without touching the wizard
maestro init -y \
  --name my-project \
  --repo-owner yourname \
  --doppler-project my-project \
  --ci --license

# Generate an HTTP service with Postgres in Docker
maestro service api -y --shape http --db postgres

# Generate a worker that talks to an existing Postgres (the URL is pinged, not stored)
maestro service ingest -y \
  --shape worker --db postgres --db-mode live \
  --db-url "postgres://user:pass@host:5432/dbname"

Required with -y: --name and --repo-owner for init; --shape for service. Everything else has a sensible default - port auto-assigns to the next free slot in the shape's range, --db defaults to none, --db-mode defaults to docker. Live URLs are pinged before scaffolding; a failed ping is a hard error.

Commands

Command Purpose
maestro init Scaffold a new workspace (interactive wizard).
maestro service <name> Generate a new service (interactive shape + DB picker).
maestro preset apply <preset> <service> Scaffold a curated feature (e.g. auth-jwt, api-gateway) and deposit its reusable libs into common/go. See docs/PRESETS.md.
maestro preset list List the available presets.
maestro secrets keygen Print a dev ES256 keypair + app secrets to load into a provider or .secrets/.
maestro refresh Reconcile generated files with project.toml. Preserves user edits.
maestro rename <old> <new> Rename a service across the workspace — directory, module path, sibling imports, Dockerfiles, Air configs, project.toml, go.work. Reverse with maestro rename <new> <old>.
maestro delete <name> Remove a service — its directory, its go.work entry, its compose entries, its project.toml section.
maestro doctor Verify that git, task, go, docker, air (and sqlc/goose/buf when relevant) are on PATH.

What gets generated

After maestro init + maestro service api --shape=http:

my-project/
├── project.toml            # source of truth for `refresh`
├── go.work                 # multi-module workspace
├── docker-compose.yaml     # prod compose, regenerated by refresh
├── docker-compose.dev.yaml # dev compose with bind-mounts + Air
├── Taskfile.yaml           # root Taskfile, regenerated by refresh
├── Taskfile.local.yaml     # optional, user-owned, never touched
├── .gitignore              # includes .secrets/, tmp/, etc.
├── common/go/              # shared module — imports resolve here under Docker too
└── api/
    ├── cmd/api/main.go
    ├── internal/server/    # Gin + zap, /health + /ready
    ├── docker/
    │   ├── dev.Dockerfile
    │   └── prod.Dockerfile
    ├── go.mod              # with replace ../common/go
    └── Taskfile.yaml       # api:dev, api:up, api:down, api:proto:generate, …

Add --shape=grpc and you also get buf.yaml, buf.gen.yaml, and proto/<ident>/v1/<ident>.proto in a layout that satisfies buf lint (PACKAGE_DIRECTORY_MATCH).

Add a database and you also get sqlc.yaml, internal/db/ (with a hand-authored conn.go exposing db.Open(ctx, url); sqlc's generated code lands alongside it as db.go + models.go + queries.sql.go when you run task <svc>:generate), internal/migrate/, and - if you chose Docker mode - a <name>-db sidecar with a managed DATABASE_URL in the compose env.

The DB workflow is migrate-first: sqlc reads its schema from internal/migrate/, so the cycle is task <svc>:migrate:create -- <name> → write your CREATE TABLE … into the generated file → task <svc>:migrate:up → write queries into internal/db/queries.sql → task <svc>:generate. Writing a query against a table that has no migration is the most common cause of a confusing sqlc generate error.

Every file in a scaffolded project falls into one of three categories - managed (regenerated by refresh), user-owned (scaffolded once, then yours), or project.toml (the source of truth you edit by hand). The full inventory lives in docs/MANAGED_FILES.md.

Service shapes

Shape Generated
bare cmd/<svc>/main.go printing "Hello".
http Gin server with /health, /ready, structured request logging via zap, graceful shutdown.
grpc grpc.NewServer() with health + reflection, buf-managed proto layout, signal-driven GracefulStop.
worker Long-running loop over ctx.Done() + ticker, exits cleanly on SIGINT/SIGTERM.

All shapes use zap.NewProduction() with ReplaceGlobals so libraries can log via zap.L().

Presets

Shapes are empty skeletons; presets are working features scaffolded on top. Applying one (maestro preset apply <preset> <service>) generates the feature's services and deposits the reusable libraries it's built from into your common/go, so you build your own services on those building blocks rather than being locked into the preset's code.

Preset What it scaffolds
auth-jwt JWT auth service — email-OTP verify, password + Google sign-in, MFA, MFA-gated staff roles with RBAC, refresh rotation — plus a cleanup worker.
api-gateway Stateless edge — reverse proxy, edge JWT validation, signed gateway-to-service identity via internalauth, Redis rate limiting, CORS, API versioning.

The end-to-end walkthrough — standing up auth behind the gateway, running it out of the box, and writing your own service that reads the gateway-injected caller via the identity lib — is in docs/PRESETS.md.

Configuration: project.toml

[project]
  name = "my-project"
  repo_owner = "yourname"

[dev]
  containerized = true   # generate Dockerfiles + compose entries
  hot_reload = true      # generate .air.toml + Air step in dev compose

[secrets]
  provider = "doppler"   # or omit / "none"
  doppler_project = "my-project"

[service]
  [service.api]
    shape = "http"
    db = "postgres"
    db_mode = "docker"
  [service.worker]
    shape = "worker"

Edit it, run maestro refresh, and every managed file catches up. The wizards write this file for you; or you can also edit it by hand.

Requirements

maestro doctor will tell you what's missing. The full set:

  • Required: Git, Task, Go, Docker, Air
  • For DB services: sqlc, goose
  • For gRPC services: buf

Status

Pre-1.0 and MIT licensed. The core flow (init → service → refresh → up) is solid and covered by both unit tests and end-to-end smoke tests (go test -tags=e2e ./...), including a refresh-idempotency invariant that fails if a second refresh against a freshly-scaffolded project produces any diff at all. Per-service Dockerfiles are scaffolded once and treated as user-owned thereafter - refresh warns when they no longer reflect [dev] hot_reload or [service.<name>] db but never rewrites them; regenerating is maestro delete <svc> && maestro service <svc>. maestro rename is its own inverse - a partial run is reversed by running it backwards. Pieces still on the roadmap:

  • Partial-failure rollback in service if a downstream step bombs mid-scaffold

License

MIT — see LICENSE. Copyright © 2026 Anze and Zagforge.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages