---
metadata:
    description: Millstream runs pipelines that mix code, LLM agents and human review. Durable and observable, from one binary on a laptop to a server, and they learn from production.
    og_type: website
    title: Durable pipelines for code, agents and people
---

# Durable pipelines for code, agents and people.

Millstream runs the work your team builds around LLMs as a declarative graph of steps over persisted streams of
items. A crawler streams records, an agent scores each one, a filter keeps the good ones, an analyst approves and a
report goes out. Every item is stored, every agent call is costed, and the pipeline learns from what happens in
production.

[Read the quick start](/docs/getting-started) [See how pipelines learn](/platform/self-improvement)

- One Go binary, from SQLite on a laptop to Postgres on a server
- Actions in any language, agents on Anthropic or any OpenAI-compatible model
- Open-source core under Apache-2.0

<div class="millrace" role="img" aria-label="An example pipeline: a daily trigger, a crawler, a deduplication, an agent that scores each listing, a filter, an analyst's review and a report, with the number of items left after each step.">
<div class="mr-head"><strong>property-sourcing</strong><span class="mr-live">An example day of the reference pipeline</span></div>
<div class="mr-flow">
<div class="mr-step" data-kind="trigger"><span class="mr-kind">Trigger</span><span class="mr-name">daily</span><span class="mr-fig"><b>06:00</b> Europe/Paris</span></div>
<div class="mr-chan" aria-hidden="true"><i></i><i></i><i></i></div>
<div class="mr-step" data-kind="code"><span class="mr-kind">Code</span><span class="mr-name">crawl</span><span class="mr-fig"><b>1,800</b> listings</span></div>
<div class="mr-chan" aria-hidden="true"><i></i><i></i><i></i></div>
<div class="mr-step" data-kind="builtin"><span class="mr-kind">Builtin</span><span class="mr-name">dedupe</span><span class="mr-fig"><b>1,240</b> unique</span></div>
<div class="mr-chan" aria-hidden="true"><i></i><i></i><i></i></div>
<div class="mr-step" data-kind="agent"><span class="mr-kind">Agent</span><span class="mr-name">score</span><span class="mr-fig"><b>$14</b> for 1,240</span></div>
<div class="mr-chan" aria-hidden="true"><i></i><i></i><i></i></div>
<div class="mr-step" data-kind="builtin"><span class="mr-kind">Filter</span><span class="mr-name">keep-good</span><span class="mr-fig"><b>310</b> score ≥ 70</span></div>
<div class="mr-chan slow to-person" aria-hidden="true"><i></i><i></i></div>
<div class="mr-step" data-kind="person"><span class="mr-kind">Person</span><span class="mr-name">analyst-review</span><span class="mr-fig"><b>35</b> of 42 approved</span></div>
<div class="mr-chan slower" aria-hidden="true"><i></i></div>
<div class="mr-step" data-kind="code"><span class="mr-kind">Code</span><span class="mr-name">report</span><span class="mr-fig"><b>35</b> PDFs</span></div>
</div>
<div class="mr-legend" aria-hidden="true"><span>Code, in any language</span><span class="lg-builtin">Builtin, run by the engine</span><span class="lg-agent">LLM agent</span><span class="lg-person">A person decides</span></div>
</div>

---

## Steps know streams, never each other

A pipeline is one YAML file. Steps read streams and write streams; they never call each other. That makes the graph
easy to change: several crawlers write one stream, several consumers each receive every item, and inserting a step
means pointing it at a stream and re-pointing one consumer.

```yaml
pipeline: property-sourcing

vars: { minScore: 70 }
triggers:
  daily: { cron: "0 6 * * *", tz: Europe/Paris }

steps:
  crawl:
    action: lab/crawl-listings@1.2
    in: trigger.daily
    out: listings.raw
  dedupe:
    action: dedupe
    in: listings.raw
    out: listings.unique
    config: { key: "item.addressNorm + ':' + string(item.surfaceM2)", window: 30d }
  score:
    action: lab/score-listing@2    # an agent
    in: listings.unique
    out: listings.scored
    concurrency: 8
  keep-good:
    action: filter
    in: listings.scored
    out: listings.shortlist
    config: { expr: "item.score >= vars.minScore" }
```

`millstream check` validates the whole package before it runs: schemas between steps, CEL expressions, secrets and
actions. There is no version field to bump, because each distinct package content is a revision and every run
records the one it used. When a step changes, replay it over past items and compare before you ship:

```bash
millstream replay --pipeline property-sourcing --step keep-good --since 7d --var minScore=75
```

[More on pipelines](/platform/pipelines)

---

## An agent is an action like any other

An agent step declares its prompt, its model, its output schema and what it may touch. Millstream renders the
prompt with the item, runs the model in a sandbox, validates the answer against the schema, and records the
transcript, the tokens and the cost.

```yaml
name: lab/score-listing
version: 2.1.0
kind: agent
output: { $ref: schema/output.json }
runtime:
  agent:
    harness: claude-code          # or api: Anthropic, or any OpenAI-compatible endpoint
    model: claude-sonnet-5-5
    tools: [WebFetch]
    network: [api.anthropic.com]
    maxTurns: 20
    maxCostUsd: 0.50
```

- **Schema-validated output.** An answer that does not match is a retryable error, and never reaches the next step.
- **Budgets that stop spending.** Per delivery, per run and per day, enforced by reservation before a call starts.
- **Every attempt transcribed**, secrets redacted, kept with the delivery that produced it.
- **Record and replay.** Tests, evals and CI replay recorded model calls offline, with no API key.

[More on agents](/platform/agents)

---

## People decide where it matters

A `review` step parks each item in an inbox until someone approves, edits or rejects it. Every decision records
who, when, why and the diff of any edit, and feeds the pipeline's learning.

```yaml
analyst-review:
  action: review
  in: listings.selected
  out: listings.approved
  config:
    inbox: analysts
    timeout: 72h
    onTimeout: { escalate: leads }
    editable: [/marginPct, /notes]
    reasons: [overpriced, badLocation, needsVisit]
    autoApprove: "item.marginPct >= 25"
    sampleRate: 0.05
```

Rules approve what is obvious, and a sample of what they approve still goes to a person, so a rule that stops being
right shows up as a measured drop in agreement. The headline figure is the **intervention rate**: how often a person
had to step in, per step and per pipeline.

[More on human review](/platform/review)

---

## The cheapest step that works

Each step runs a rule, code, an agent or a person, and the pipeline file says which. As reviews and deliveries
accumulate, Millstream proposes moving a step to a cheaper kind: code in place of an agent, with the agent kept as
its fallback, or an approval rule in place of a review, with a sample still checked by a person. Each proposal is
checked against past items before anyone accepts it.

### A rule

- `filter`, `dedupe` and routes, written in CEL
- Run by the engine, at no cost per item
- Every threshold visible in the pipeline file

### Code

- A program in any language, or a container
- For rules that are known, however long
- Abstains on what it cannot decide, for a fallback

### An agent

- For rules nobody has written yet, and free text
- A schema, a sandbox, a budget and a transcript per call
- Proposed for replacement by code once its answers are predictable

### A person

- For judgement, and for effects someone must authorise
- Inboxes, edits and reason codes
- An approval rule proposed where approvals are near-universal

---

## Pipelines that learn from production

Millstream watches every run and raises insights with no LLM involved: slow steps, failure clusters, cost per
approved item, what reviewers reject and which fields they edit, items an agent paid for that a later filter
discarded. In a development deployment, the `improve` pipeline drafts fixes from that evidence and validates them
before a person sees them.

- **Insights** come from the event log, in every deployment.
- **Evidence** leaves production as a redacted bundle that expires.
- **Proposals** are drafted in development and backtested against that evidence: a lessons file, a prompt, a
  threshold, a filter moved upstream, or code that replaces an agent.
- **Nothing ships unreviewed.** A person accepts each proposal, and it reaches production as a commit or a pull
  request through the normal release.

When an agent's answers have become predictable, Millstream proposes code that does the same job, keeps the agent as
the fallback for the items the code is unsure of, and proves it on a holdout the drafting agent never saw. The
platform's own acceptance test measured this end to end, on synthetic listings whose answers follow a rule:

- **5.2%** of the agent cost the day after distillation, audits included
- **1.000** agreement on 260 held-out items
- **0.58 → 1.00** reviewer agreement after a lessons proposal

[How pipelines learn](/platform/self-improvement) [Read the article](/blog/self-improving-pipelines)

---

## Every item has a history

Each item is immutable, carries a deterministic id, and links to the items it came from and the items it produced.
Every state change is an event, and the metrics, run reports, insights and the live UI are all projections of that
one log.

```bash
millstream run report listings run_replay_20261020T162620Z-b9df9171
```

```text
Run run_replay_20261020T162620Z-b9df9171 of listings (replay): succeeded
Started 2026-10-20T16:26:20Z, settled 2026-10-20T16:26:21Z, 540ms, cost $0.0000
Items: listings.ready 1, listings.renovate 1

Critical path (540ms):
  STEP        DELIVERY                             LATENCY  WAIT   WORK  BACKOFF  REVIEW  SHARE
  affordable  dl_719ce262d713c5774527c64ea86e39e2  540ms    539ms  1ms   0s       0s      100%
```

The web UI ships in the open-source core: a live graph with throughput, latency and errors per step, run timelines,
item lineage with every attempt and transcript, dead letters grouped by cause, the review inbox and the insights. It
reads in English and French and meets WCAG 2.2 AA.

[More on observability](/platform/observability)

---

## Kill it at any point

Delivery is at least once, and idempotency comes from deterministic item ids: a retried step produces the same ids,
so nothing downstream sees an item twice. Outputs publish only when a delivery is done. The crash suite kills the
engine and its nodes at every boundary of every atomic unit, on SQLite and on Postgres, and checks that nothing is
lost and nothing is published twice.

The soak runs the Docker Compose deployment, an active engine, a hot standby and two nodes, while a chaos driver
kills engines and cuts nodes off the network:

- **15,837** items through 30 minutes of chaos
- **~30 ms** for the standby to take over the lock
- **0** items lost or published twice

[More on deployment](/platform/deployment)

---

## From a laptop to a server, unchanged

The same package runs everywhere; only the environment file says where. On a laptop, `millstream run` is the
engine, a node and the web UI on SQLite, with nothing to install. On a server, the same binary runs on Postgres with
a hot standby, and nodes on other hosts pull work under fenced leases.

```bash
millstream run                          # laptop: SQLite, an embedded node, the UI on 127.0.0.1:7070
docker compose up -d                    # server: Postgres, active and standby engines, two nodes
millstream node --join https://engine:7070 --labels gpu=true
```

```yaml
environment: production
store: { dsn: postgres://db.internal/millstream }
artifacts: { url: s3://millstream-artifacts }
auth: { token: { env: MILLSTREAM_AUTH_TOKEN } }
retention: { events: 90d, transcripts: 30d }
```

Each customer gets a deployment of their own, on their infrastructure. Several pipelines share it, isolated from
each other, and a pipeline can import another's stream without changing it.

[Deploy it](/platform/deployment)

---

## Write actions in any language

An action is any program that speaks JSON lines over stdin and stdout. The protocol fits on one page, a
conformance suite checks an implementation, and the Go SDK turns an action into one function.

```go
func main() {
    millstream.Map(func(ctx millstream.Context, in Listing) ([]Priced, error) {
        ctx.Metric("works_estimate_eur", float64(estimate(in)), nil)
        return []Priced{price(in, ctx.Config())}, nil
    })
}
```

```bash
millstream test action ./actions/rentability --conformance
```

[Read the action protocol](/docs/action-protocol)

---

## Built for the day it goes live

### Operations

- Prometheus metrics, run reports and an event tail
- Pause, resume, cancel and roll back from the CLI or the UI
- Dead letters with replay and discard
- Retries with backoff, timeouts, concurrency and rate limits

### Agent governance

- Tools, MCP servers and network destinations allowlisted per action
- Cost per call, per run and per day, with hard stops
- Evals with assertions and LLM judges, scored per case
- A check that warns when agent output can reach an external effect unreviewed

### Data

- Retention per stream, for payloads, transcripts and events
- Erasure by key that follows lineage across pipelines
- Secrets redacted from transcripts and recordings
- Personal data hashed out of evidence bundles

### Security

- Loopback by default, a 32-character token and TLS when exposed
- Agents and third-party actions in containers with no capabilities
- Proposed code built and run only in a sandbox with no network
- Images and registry actions pinned by digest

### Packaging

- A distroless image running as a non-root user
- Archives for Linux and macOS, amd64 and arm64, each with an SBOM
- Docker Compose and systemd units in the repository
- Signed checksums and images from the first release

---

## Open source at the core

The engine, the CLI, the Go SDK, the builtins, the agent adapter, the protocol and the whole web UI are open source
under Apache-2.0. A commercial edition adds what larger organisations pay for: single sign-on, roles per pipeline and
inbox, audit export, a catalog of production connectors, approval policies on proposals, and support.

[Compare the editions](/pricing)

---

## Build your next AI workflow on Millstream

The five milestones of the platform are built and tested, and the first open-source release is next. Get early
access to run it on your own pipelines, or read how it works first.

[Get early access](/pricing#early-access) [Read the quick start](/docs/getting-started) [Why Millstream](/why)
