Managoat is the hosted Fountain. Fountain is the open-source engine, and its name is on the CLI, the API, the SDK and this manual. Everything here applies to Managoat unless a page says it is for a self-hosted server.
About environments
This page explains what an Environment is, and why it is separate from the other three primitives. For each field, read the API reference. To bootstrap one, read the guided tour.
What an environment is
An Environment is the machine an agent wakes on, described as data.
It holds four kinds of thing.
- Encrypted secrets. Pairs of key and value, encrypted for each tenant with AES-256-GCM. They are write-only. Once you store a value, the API never returns it. A list gives you keys and timestamps.
- Plain environment variables. A non-secret
env_varsmap, for values that are not sensitive. Feature flags and endpoints go here. The API returns these as you wrote them, so a sensitive value belongs in secrets instead. - Runtime config. The packages to install, the repos to clone, and the setup script to run.
- A network policy.
networking_typeisunrestrictedorlimited.
Why it exists
What a machine holds changes on a different schedule from everything else about an agent.
A team decides Python 3.12, a checkout of your service repo and a
mise install step one time, then leaves them alone for months. Which
credentials one run uses can change each hour. Which model an agent runs is a
question you revisit every few weeks.
Put those in one object, and a token rotation edits the machine image. Each agent that shares the image then needs a new one. Divide them, and many agents can share one Environment. A single run can still deviate from it, and nobody edits the shared thing.
How it works
You attach an Environment to an Agent when you create the Agent. That Environment is the Agent's default.
It is not the only choice. A Conversation can name a different
environment_id at launch, within the Agent's allowed_environment_ids. So
the same Agent can start from a heavy image for one task and a small one for
another.
At spawn, Fountain merges the Environment's secrets with the secrets of any attached Vault. The result becomes the process environment in the sandbox. The Vault wins on a key collision. Read About vaults.
apiVersion: fountain.dev/v1
kind: Environment
metadata:
name: python-data-env
spec:
packages:
apt:
- jq
networking_type: limited
secrets:
OPENAI_API_KEY: sk-...
secrets must be a map of KEY: value. The server keeps only the map form
and drops any other shape without an error. The map form is also what the
CLI's ${VAR} substitution and secret-manager references resolve against.
packages has two keys that install software, apt and npm. Other
language toolchains go in setup_script.
The network policy is not symmetric
unrestricted does nothing. A Sprites sandbox is open by default, so the
value changes nothing.
limited holds egress to the domains in networking_config.allowed_hosts.
That is the only networking_config key Fountain reads today.
Under limited with no allowed_hosts, the allowlist is empty. Nothing gets
out. That is deliberate, and it is not a defect. An empty allowlist that
quietly meant "allow everything" would be the worst default for a policy whose
whole purpose is restriction.
Limited access
The broker is on for the hosted accounts we enrol by hand. Read Feature status.
On a hosted account with the egress credential broker on, the sandbox can
reach only the broker. The broker then applies your policy. With
unrestricted, a request goes through to any host, with only the credentials
you bound. With limited, the broker refuses a request to a host that is not
in allowed_hosts. The refusal is a 403 that names the host. The broker's
request log, GET /api/conversations/:id/egress, shows each decision. List what the setup of the sandbox needs, such as
registry.npmjs.org for the agent's adapter. A host with a bound credential,
such as the model's API, needs no entry.
Not every backend can hold egress. Sprites, E2B and Daytona can. A
self-hosted runner cannot. The agent selects the
backend, and the environment sets the policy. Fountain therefore compares the
two at launch. It refuses a limited environment on a backend that cannot
enforce one. The conversation fails with a network / failed event. The
reason is backend_lacks_network_policy, and Fountain creates no sandbox. The
agent form gives you the same warning before you save the agent.
What an environment is not
Not a deployment tier. "Environment" in Fountain never means dev, staging or production. You might build those out of Environments and Vaults. Fountain does not model them.
Not a container image. Fountain builds no image and stores no image. An Environment is a recipe that the sandbox provider applies at provision time. Two conversations from the same Environment install their packages one by one, on their own machines.
Not a secret store you can read. Values go in and never come out. To learn what a secret is, go back to the source you got it from. Fountain will not tell you.
When to use something else
Use a Vault when the value changes for each run, for each customer or at each credential rotation.
Use env_vars and not secrets when the value is not sensitive. The API
returns env_vars as you wrote them. File a non-secret as a secret, and you
get a value that nobody can ever read back to check.
Use inference credentials for model API keys. Those belong to one user. An operator never sets them, and an Environment never stores them. You set them in the console or over the inference credentials API.
Where to go next
- About vaults, the other half of the merge.
- About agents, which names an Environment.
- The guided tour, which builds one in its first step.
- Configuration reference, for the sandbox settings that belong to the instance.