The four primitives

This page explains what Fountain's four objects are, and why there are four of them. For the fields on each one, read the API reference. To build something with them, start with the guided tour.

The problem the four primitives divide up

To run a coding agent on a machine that is not yours, you must decide four things. Each of the four changes on its own schedule.

What the machine holds changes rarely. Python 3.12, a checkout of your repo, a setup script. You decide it once for a team, then leave it alone for months.

Which credentials the agent runs with changes constantly. A staging database URL today, a customer's API key tomorrow, a rotated token an hour from now.

How the agent behaves changes sometimes. Which model, which runtime, which skills, which MCP servers, and what its system prompt says.

What it does right now changes every few seconds.

Put all four in one object, and each credential rotation edits the machine image. Each prompt edits the credentials. Fountain divides them into four objects on purpose, and that division is the product.

Primitive Answers Changes
Environment what the machine holds rarely
Vault which credentials the run uses constantly
Agent how the agent behaves sometimes
Conversation what it does now continuously

How they compose

An Agent names an Environment. A Conversation runs an Agent. That Conversation can name a different Environment, and it can attach a Vault for that one run.

Three of the four are templates. They are rows that you write once and use many times. The fourth, the Conversation, is a run on a machine. The machine is the heavy work, and Fountain does it for you.

1 · WRITE ONCE · reusable rows 2 · AT LAUNCH · one API call 3 · THE MACHINE · one per launch Agent model, runtime, skills Environment packages, repos, secrets Vault secret overrides, per run Fountain builds the machine merges secrets, vault wins starts the agent's runtime Sandbox env: config + credentials real values or brokered placeholders Conversation own transcript Conversation same machine the next turn lands on it idle parks it, the ceiling reclaims Egress broker where the deployment runs one → GitHub, model APIs, yours spawns the real values HTTPS_PROXY, the only exit
Three templates, one machine. Agent, Environment and Vault are rows you write once. At launch Fountain builds the sandbox from the Environment, merges the secrets with the Vault winning, and starts the Agent's runtime. Several Conversations can share one machine, and the next turn lands on the machine the last one left. An idle machine parks and the concurrency ceiling reclaims it. Where a deployment runs the egress broker, the real values go to the broker and the sandbox gets a placeholder, such as __github_token__; the sandbox reaches the internet only through the broker, which attaches the value. Where none runs, the merged secrets enter the sandbox as environment variables.

A Conversation starts. At that moment Fountain merges the Environment's secrets with the Vault's secrets. The Vault wins on a key collision. That one rule is what makes the division into four usable and not merely tidy. About vaults sets it out.

What happens next depends on the deployment. Where no egress broker runs, the merged secrets enter the sandbox as environment variables. Where one runs, and one runs on the hosted platform, a bound secret does not. The sandbox gets a placeholder, such as __github_token__, and the broker puts the real value on each request to the bound host. Where a secret comes from follows one secret through both paths.

Why an API for this, when you can run an agent in a sandbox by hand? Because the templates outlive the run. The machine, the secrets and the agent are rows you can list, diff, share and launch from code. Because the identity and the machine are separate, so one Environment can run as you, as a bot user, or as a customer. And because a run is a thing with an id, a status, a transcript and an event stream. A webhook, a cron job or another agent can start one and follow it.

Substitution

Each string value in an Agent config takes a ${VAR} reference. Fountain resolves it from the merged environment and vault secrets at spawn time.

Syntax Result
${VAR} The value of VAR from the merged map
$$ A literal $

Substitution is recursive, so it works inside maps and lists. It is also fail-complete. Fountain reports each absent variable at once, and not one for each attempt.

What Fountain does not have

There is no fifth primitive. Two things look like one and are not.

A team is not an object. A teammate is a Conversation bound to the reserved channel fountain:team. Read Agents as teammates.

A sandbox is not an object you create. Fountain gives you one when a Conversation starts. An ephemeral sandbox ends with its Conversation, and a persistent one stays as the Agent's own machine. You can list a sandbox, put a second Conversation on it with sandbox_id, and reset a persistent one. Read About sandboxes and the Sandboxes section of the API reference.

Where to go next