# Swamp tutorial: make AI agent work repeatable

> Learn how Swamp turns one-off AI agent work into typed, repeatable automation with models, workflows, assertions, and versioned data.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-08-22 | Topics: [AI](https://flaviocopes.com/tags/ai/) | Canonical: https://flaviocopes.com/swamp/

An AI agent can solve a difficult task once, then solve it differently tomorrow.

The second solution may still work. Or it may skip a check, use another tool, or forget an important detail that only existed in the previous conversation.

[Swamp](https://swamp-club.com/) is built for this problem.

You let the agent figure out the task. Swamp turns the result into typed models and workflows that run the same way next time.

The example that made it click for me was a personal voice recorder. A spoken note could become a reminder, a research task, or a coding job. An agent decided what the note meant, then Swamp ran the right automation and updated the data.

That split is the important part:

```text
Agent decides what to build
        ↓
Swamp runs it repeatably
        ↓
Versioned data records what happened
```

Let's see how Swamp works, then build a small automation with it.

## What Swamp is

Swamp is an open source automation runtime designed for AI agents.

It is not another chat interface. It is also not an agent model.

It runs under the agent as a command-line tool. Codex, Claude Code, Cursor, or another coding agent can use it to create and run automation.

Swamp gives the agent a small set of durable building blocks:

- **Models** describe one typed operation
- **Workflows** connect operations into a dependency graph
- **Data** stores versioned outputs from every run
- **Vaults** keep credentials out of files and logs
- **Extensions** package reusable models and integrations
- **Skills** teach the coding agent how to use all of this

If you know [how MCP works](https://flaviocopes.com/what-is-mcp/), the difference is useful.

MCP exposes tools to an agent. Swamp gives the agent an execution layer where it can turn successful work into a repeatable system.

You can use both. They solve different problems.

## The idea behind models

A Swamp model has two parts.

The **model type** contains the reusable logic. It defines the accepted inputs, available methods, and produced data.

The **model definition** configures one use of that type.

Suppose we create an HTTP check type. The type knows how to request a URL and measure its response.

We can then create several definitions from it:

```text
HTTP check type
  ├── check-primary → https://example.com
  └── check-backup  → https://example.org
```

The behavior stays in one place. Each definition only supplies a URL and timeout.

This is more useful than asking an agent to invent two separate checks. The model type becomes a reusable capability.

## Install Swamp

Swamp supports macOS and Linux.

Install the CLI using the official script:

```bash
curl -fsSL https://swamp-club.com/install.sh | sh
```

If you do not run remote scripts directly, open the script in your browser and inspect it first.

Restart your shell, then verify the installation:

```bash
swamp version
```

The binary is installed in `~/.local/bin`. Add that directory to your `PATH` if the command is not found.

## Create a Swamp repository for Codex

Create a new Git repository for the tutorial:

```bash
mkdir swamp-endpoint-check
cd swamp-endpoint-check
git init
```

Initialize Swamp and tell it we use Codex:

```bash
swamp repo init --tool codex
```

Swamp creates a `.swamp.yaml` file, plus directories for models, workflows, vaults, and runtime data.

It also adds Swamp instructions to `AGENTS.md` and installs its skills under `.agents/skills/`.

Open Codex in this directory. You can now ask it to get started with Swamp:

```text
$swamp-getting-started
```

The skill gives Codex the current commands and file formats. This matters because Swamp expects the agent to operate the framework for you.

We will still create the pieces by hand in this tutorial. I want you to see what the agent will build.

## Create a typed HTTP check

Create `extensions/models/http_check.ts`.

This file defines one model type with a `check` method:

```ts
import { z } from 'npm:zod@4'

const GlobalArguments = z.object({
  url: z.string().url(),
  timeout: z.number().positive().default(5000),
})

const Result = z.object({
  status: z.number(),
  latency: z.number(),
  ok: z.boolean(),
})

export const model = {
  type: '@local/http-check',
  version: '2026.08.21.1',

  globalArguments: GlobalArguments,

  resources: {
    result: {
      description: 'HTTP endpoint check result',
      schema: Result,
      lifetime: 'infinite' as const,
      garbageCollection: 100,
    },
  },

  methods: {
    check: {
      description: 'Request the endpoint and record its status',
      arguments: z.object({}),

      async execute(
        _args: Record<string, never>,
        context: any,
      ) {
        const { url, timeout } = context.globalArgs
        const startedAt = Date.now()

        let status = 0
        let ok = false

        try {
          const response = await fetch(url, {
            signal: AbortSignal.timeout(timeout),
          })

          status = response.status
          ok = response.ok
        } catch {
          // A network error stays status 0 and ok false
        }

        const result = {
          status,
          latency: Date.now() - startedAt,
          ok,
        }

        const handle = await context.writeResource(
          'result',
          'result',
          result,
        )

        return { dataHandles: [handle] }
      },
    },
  },
}
```

The Zod schemas are not only documentation. Swamp uses them to validate arguments and saved results.

The `result` resource has an infinite lifetime and keeps its latest 100 versions. We will query those versions later.

Check that Swamp can find the type:

```bash
swamp model type describe @local/http-check
```

You should see the `url` and `timeout` arguments, the `result` resource, and the `check` method.

## Create two model definitions

Now create two endpoint checks from the same type:

```bash
swamp model create @local/http-check check-primary \
  --global-arg 'url=https://example.com' \
  --global-arg 'timeout=5000'
```

Create the second definition:

```bash
swamp model create @local/http-check check-backup \
  --global-arg 'url=https://example.org' \
  --global-arg 'timeout=5000'
```

Both definitions use the same code. Their configuration is stored as YAML under `models/`, where you can review and commit it.

Run one check directly:

```bash
swamp model method run check-primary check
```

Swamp prints the arguments and the typed data produced by the run.

Inspect the stored result:

```bash
swamp data get check-primary result
```

The output looks like this:

```json
{
  "status": 200,
  "latency": 184,
  "ok": true
}
```

The latency will be different on your machine.

## Connect the checks in a workflow

A workflow is a directed acyclic graph, or DAG.

Steps without dependencies run in parallel. Later jobs can wait for earlier jobs to complete.

Create an empty workflow:

```bash
swamp workflow create endpoint-check
```

Swamp creates a YAML file under `workflows/`. Open it, keep its generated `id`, and replace the rest with this:

```yaml
id: <keep the generated id>
name: endpoint-check
tags:
  purpose: tutorial
jobs:
  - name: collect
    description: Check both endpoints in parallel
    steps:
      - name: primary
        task:
          type: model_method
          modelIdOrName: check-primary
          methodName: check
      - name: backup
        task:
          type: model_method
          modelIdOrName: check-backup
          methodName: check

  - name: verify
    description: Verify the collected results
    dependsOn:
      - job: collect
        condition:
          type: completed
    steps:
      - name: primary-is-healthy
        task:
          type: assert
          expr: data.latest("check-primary", "result").attributes.ok == true
          message: The primary endpoint must be healthy
          severity: high
      - name: backup-is-healthy
        task:
          type: assert
          expr: data.latest("check-backup", "result").attributes.ok == true
          message: The backup endpoint must be healthy
          severity: high
version: 1
```

The two steps in `collect` have no dependency between them. Swamp can run them at the same time.

The `verify` job waits for `collect`. Its assertions read the latest typed results using CEL expressions.

Validate the workflow before running it:

```bash
swamp workflow validate endpoint-check
```

You can also inspect its graph:

```bash
swamp workflow get endpoint-check --graph
```

Now run it:

```bash
swamp workflow run endpoint-check
```

The workflow succeeds when both endpoints return a successful HTTP status.

## Make the workflow fail on purpose

Open the model definition containing `name: check-backup` under `models/`.

Change its URL to a local port that should not be listening:

```yaml
globalArguments:
  url: http://localhost:9
  timeout: 2000
```

Run the workflow again:

```bash
swamp workflow run endpoint-check
```

The HTTP model still produces a valid result:

```json
{
  "status": 0,
  "latency": 2,
  "ok": false
}
```

Then the assertion fails the workflow.

This separation is useful. The model records what happened. The workflow decides whether that state is acceptable.

## Query the run history

Every check produced another version of `result`.

List the versions for the backup check:

```bash
swamp data versions check-backup result
```

Now search all stored data for failed checks:

```bash
swamp data query 'attributes.ok == false'
```

You do not need to parse logs or ask an agent what happened. The result is structured data with a schema, version, timestamp, and model identity.

This is one of the strongest ideas in Swamp.

The output of automation becomes input for later automation.

## Let the agent extend the system

Now that the repository contains a working pattern, you can ask Codex to change it.

For example:

```text
Add a third endpoint to the endpoint-check workflow.
Run all HTTP checks in parallel.
Add an assertion that fails when latency is over 1000ms.
Validate the models and workflow, then show me the graph.
```

Codex can read the installed Swamp skill, inspect the existing model type, create the new definition, update the workflow, and run validation.

The agent still reasons freely while building the change.

Once the YAML and TypeScript are saved, the next run follows those definitions. It does not need to rediscover the process from chat history.

## Where vaults fit

Our endpoints are public, so the tutorial does not need credentials.

Real automation usually does.

Do not put API keys inside model definitions or workflow YAML. Swamp vaults keep secrets on a separate path and expose them through expressions such as:

```text
vault.get('production', 'api-token')
```

Swamp can redact secret values from output and keep credential access separate from the versioned data layer.

This is important when an agent builds the automation. The agent needs permission to use a credential without copying it into generated files or logs.

## How I would use Swamp

I would start with release verification for the software I publish through [Prototyped](https://prototyped.dev).

Each project needs a slightly different release process, but the shape repeats:

1. Run tests.
2. Build the release artifact.
3. Check the source package for private files and secrets.
4. Verify the documentation and checksums.
5. Run a smoke test.
6. Record the result.

An agent is useful when creating that workflow for a new project. It can inspect the repository and work out which commands and checks apply.

After that, I want the release check to be boring.

I do not want the agent to invent a new verification process every time I publish an update. I want the same typed steps, the same gates, and a clear history of every result.

I would not move every existing script into Swamp.

If a small shell script already does one stable job well, I would keep it. Swamp becomes interesting when the task crosses several systems, the agent is discovering integrations, the result must be inspected later, or several people need to share the automation.

## What to watch out for

Swamp is a large system for a specific problem.

It makes sense when you need repeatability, typed outputs, secrets, history, parallel work, or reusable integrations.

It is too much for a one-line cron job.

Also remember that an extension is executable code. Review third-party extensions before trusting them, just as you would review a package or an agent tool.

Swamp is licensed under AGPL-3.0 with an additional extension and definition exception. Check the current license and pricing before using it inside a commercial team or service.

Finally, deterministic execution does not make a bad workflow correct.

Swamp will repeat the model and workflow you give it. Tests, assertions, narrow credentials, and human approval still matter for dangerous actions.

I wrote more about that boundary in [how to let an AI agent perform irreversible actions safely](https://flaviocopes.com/ai-agent-irreversible-actions-safely/).

## The useful mental model

The simplest way to understand Swamp is this:

> Use the agent to build the machine. Use Swamp to run the machine.

The agent remains useful where judgment and adaptation matter.

Swamp takes over where repetition, validation, and history matter.

That is a good boundary for AI automation.
