FlowForge is a Go workflow automation prototype. It loads declarative YAML workflows, validates them against a plugin catalog, executes registered operations, and emits structured execution events.
The current project is an MVP focused on the workflow kernel and local execution model. The HTTP API, persistent stores, and workflow generation service are intentionally thin placeholders for future implementation.
- Defines workflows as declarative YAML resources.
- Registers trigger and operation plugins in a runtime registry.
- Validates workflow references, required operation inputs, expressions, and side-effect warnings.
- Resolves
$path.to.valuereferences from the workflow scope before invoking operations. - Executes conditional, nested steps with
thenandelsebranches. - Supports
dry_runandliverun modes. - Emits run, step, and operation lifecycle events.
- Renders workflows into human-readable pseudocode.
cmd/server/ Demo executable entry point
examples/ Example workflow and integration-style test
internal/api/ HTTP resource and webhook handlers
internal/app/ Application service layer
internal/catalog/ Plugin manifests and schema registry
internal/kernel/ Workflow model, expression evaluator, resolver, engine
internal/plugins/ Built-in example plugins
internal/render/ Workflow pseudocode renderer
internal/runner/ Execution wrapper and event collection
internal/secrets/ Native secret model, validation, encryption, and store boundary
internal/store/ Store interfaces and future SQLite package
internal/validation/ Workflow validator
docs/ Detailed project documentation
- Go 1.25.9 or newer, matching
go.mod
Run the tests:
go test ./...Run the demo:
go run ./cmd/serverThe demo loads examples/important_messages.yaml, prints pseudocode for the workflow, and dry-runs it with a sample Telegram message event.
apiVersion: flowforge/v1alpha1
kind: Workflow
metadata:
name: important-messages
spec:
trigger:
type: telegram.message
as: message
steps:
- id: check_sender
if: "message.sender_id in inputs.target_contacts"
then:
- id: analyze
do: ai.importance
as: analysis
with:
text: "$message.text"
context: "$inputs.business_context"
- id: send_admin
if: "analysis.important"
do: telegram.send
with:
to: "$inputs.admin"
text: "$message.text"| Plugin | Type | Purpose |
|---|---|---|
| Telegram | telegram.message trigger |
Represents an incoming Telegram message event. |
| Telegram | telegram.send operation |
Sends a Telegram Bot API sendMessage request in live mode and returns a message id. |
| AI | ai.importance operation |
Classifies a message as important using a deterministic keyword-based implementation. |
| Storage | storage.save operation |
Saves a value to in-memory storage in live mode. |
cmd/serveris a demo command, not a production HTTP server.internal/store/sqliteis reserved for a future SQLite-backed implementation.- Some built-in plugins are local examples rather than production integrations.
dry_runprevents side effects in the provided side-effecting plugins, but operation implementations are responsible for honoring the run mode.
Use the same manifest command to create a resource or update an existing one:
go run ./cmd/flowforge apply -f examples/plugins/telegram.yaml
go run ./cmd/flowforge apply -f examples/secrets/telegram-proxy.yamlGet a single resource:
go run ./cmd/flowforge get workflow telegram-echo
go run ./cmd/flowforge get secret telegram-proxyList resources:
go run ./cmd/flowforge get workflows
go run ./cmd/flowforge get secretsThe default output is formatted JSON. Use -o yaml when YAML is preferred:
go run ./cmd/flowforge get workflow telegram-echo -o yamlDelete a workflow or secret by kind and name:
go run ./cmd/flowforge delete workflow telegram-echo
go run ./cmd/flowforge delete secret telegram-proxyYou can also identify the resource from its manifest. Both forms are accepted:
go run ./cmd/flowforge delete -f examples/secrets/telegram-proxy.yaml
go run ./cmd/flowforge delete secret examples/secrets/telegram-proxy.yamlWorkflow and Secret resources support create, list, get, update, and delete through their /v1 endpoints. Workflows and secrets are persisted under the server's --data-dir. Secret responses never return stored values.
Direct updates to a secret with immutable: true are rejected. The apply command uses an explicit atomic replacement when its manifest changes, so declarative updates still take effect without a delete/create gap.
Build the CLI as an executable. The cmd/flowforge directory itself cannot be executed:
mkdir -p bin
go build -o bin/flowforge ./cmd/flowforgeInstall fish completion permanently:
bin/flowforge completion install fishStart a new fish session, or load it immediately:
source ~/.config/fish/completions/flowforge.fishThe fish completion suggests commands, resource kinds, output formats, manifest paths, and resource names returned by the running server. Add bin to PATH if you want to invoke the command as flowforge. Completion generators are also available for bash and zsh:
flowforge completion bash
flowforge completion zshtelegram.send can read the bot token from the encrypted secret store with SecretRef{Name: "telegram-bot", Key: "api-key"} when a workflow runs in live mode. TELEGRAM_BOT_TOKEN remains available as a fallback. Keep the token out of workflow YAML and pass chat ids through runtime inputs, for example inputs.admin.
The optional proxy can also come from a secret, for example SecretRef{Name: "telegram-proxy", Key: "url"}. TELEGRAM_PROXY_URL remains available as a fallback. Supported schemes are http, https, and socks5; keep proxy credentials out of workflow YAML.
Start the HTTP server. Telegram polling is enabled by default and waits until the bot token secret exists:
go run ./cmd/server -addr 127.0.0.1:8080 -data-dir .flowforgeApply the echo workflow and encrypted bot token from manifest files:
go run ./cmd/flowforge apply -f examples/plugins/telegram.yaml
go run ./cmd/flowforge apply -f examples/secrets/telegram-bot.yamlIf you need a proxy:
go run ./cmd/flowforge apply -f examples/secrets/telegram-proxy.yamlSend a text message to the bot. FlowForge polls Telegram with getUpdates, converts text messages into telegram.message events, and runs matching workflows in live mode.