Environment inventory
Use this inventory to find a configuration value, see which component reads it, and learn where to set it. When application defaults differ from the supported Compose deployment, both are listed.
For local setup, start with the developer guide. For a deployment, start with the control-plane configuration and host configuration. The release contract describes packaged artifacts. Names inherited from HybridInference remain in use; NEW and MOVED mark earlier changes, not provisioning status.
Keep deployment values outside this public repository.
Store secrets in the operator’s private environment/unit files or read-only key mounts. Keep actual identifiers, hostnames, port allocations, tunnel topology and app registrations in the private deployment overlay. Public examples use placeholders such as
agents.example.org; documented protocol ports and defaults are safe to retain here.
Settings and bootstrap configuration
Sandbox Manager naming compatibility
The service is named Sandbox Manager (sandbox-manager in Compose and
cloud_agent_host.sandbox_manager in Python). Use AGENT_SANDBOX_MANAGER_*
names for new deployments. Existing values remain accepted:
Current name |
Previous name |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The supported host and local deployment scripts normalize the old token before
invoking Compose. For a direct docker compose invocation, provide
AGENT_SANDBOX_MANAGER_TOKEN; the recipe still refuses to render without it.
Host activation drains and removes the old service before creating its
replacement. Its old DNS alias and Python entry point remain available for
mixed-version deployments. Saved credentials keep their existing encryption
domain; no secret rotation or database migration is needed for this rename.
The physical broker_url database column, old host wire fields, and telemetry
producer ID remain compatibility details. New API fields and code use
sandbox_manager_url; the UI displays Sandbox Manager, including for old
measurement records.
Durable archive markers and deletion receipts also keep their existing paths
(/var/lib/freeinference-broker inside retained sandboxes and
/var/lib/hybridinference/broker-archives inside the manager). Renaming these
would hide existing lifecycle fences or pending deletion receipts.
Choose the configuration location by what the value controls:
Use Admin → Settings for application behavior, integration URLs and credentials, publication, web appearance, operational intervals, upload and skill limits, task titles, and global sandbox/workspace limits.
Use a server’s Servers page for sandbox and workspace values that should apply only to that server.
Keep bootstrap configuration in deployment files. This includes database access, encryption roots, recovery access, host provisioning, networking, isolation, image/build routing, and mounted paths.
Saved application settings live in the Agent database and remain authoritative
across restarts. The inventory below records built-in defaults and the
environment names used for an initial import. Once a setting is registered, the
service no longer reads it continuously from .env.
Run alembic upgrade head (through 0080_user_ssh_servers) before
starting this release. The updated worker claim query requires
agent_jobs.claim_retry_at, and chat plugin selections require
agent_threads.plugins; existing queued jobs remain immediately eligible and
existing chats default to no plugins after the migrations. Migration 0049
starts every existing host without a deployment drain owner or release receipt;
0051 backfills a non-null drain-fence epoch for every host. The database rotates
that epoch whenever drain-relevant lifecycle, credential, placement, or
ownership state changes. Once the V2 control plane is activated, 0051 and
AGENT_DEPLOY_DRAIN_FENCE_V2 are the application and schema rollback floor.
The V1 marker remains only so an older orchestrator can perform the bridge
upgrade; it is not a post-activation rollback target. Rebuild the sandbox image
to load selected plugins with the updated CLI wrappers. The main control-plane
listener then initializes the settings document once from its existing
environment. The
MCP-only listener
waits for that document and uses the same saved values.
After the initial import, edit values in Admin → Settings. Later environment edits do not overwrite saved values. Reset restores a setting’s built-in default, including an empty default for a credential; it does not restore an old environment value. Listeners synchronize changes every five seconds, and the web server reads saved appearance settings for every request without rebuilding or restarting the web image.
For each host sandbox or workspace setting, the service uses the first available value in this order:
the override saved for that server;
the global value saved by an administrator;
the built-in default.
Migration 0036_host_settings_import preserves valid nondefault legacy host
values as per-server overrides when an administrator has not already configured
them. Hosts without a complete report finish this import through their initial
heartbeats. An admin edit or reset excludes only the changed keys from pending
imports, so unrelated legacy values are preserved. Global exclusions
also apply to newly enrolled hosts. Later environment changes are ignored.
Clearing an override falls through to the global value or built-in default; it
never reactivates an old environment value.
Configure sandbox archives only in Admin → Settings → Sandbox. Cloud
Agent stores them as one revisioned policy in the Agent database. There is no
AGENT_SANDBOX_ARCHIVE_* environment surface. The destination is fixed at
s3://freeinference/cloud-agent/sandbox. The saved policy contains enablement,
region, bounded upload sizes, and either an AWS access key or the workload
identity fields (customer-managed KMS key ARN and expected bucket owner). The
secret access key is encrypted with the settings encryption key and is not
returned by the admin API. With no access key, sandbox managers authenticate
with their deployment workload identity through the standard AWS SDK credential
chain. See Sandbox archives for IAM,
bucket versioning, rollout, and lifecycle semantics.
The database also stores the control-plane runtime identity, signing/encryption keys, sandbox manager credentials, cookie/proxy policy, MCP target policy, and default execution profile. During an upgrade, the main listener imports only newly migrated fields that are missing. It never overwrites an existing value, including an empty reset, and the relay waits for the import to finish. The identity issuer stays pinned to this database. Existing user/compute encryption keys cannot be replaced or reset through the settings editor without a separate re-encryption procedure.
The Settings page identifies missing required fields and incomplete optional
integrations. If Gateway sign-in is not ready, an administrator can open
<web base path>/setup with ADMIN_TOKEN. Setup uses the same authenticated,
audited settings API. The recovery token stays in page memory and is never saved
in browser storage.
Keep bootstrap configuration in deployment files: database access; the settings
encryption root (AGENT_SETTINGS_ENCRYPTION_KEY, with the legacy environment
AGENT_SESSION_SECRET fallback); administrator recovery access (ADMIN_TOKEN);
host enrollment/sandbox manager provisioning; networking; tunnels/listeners; actual host
isolation; image/build routing; mounted paths; and trusted compute deployment
profiles. These values establish database access or configure processes outside
the control plane. Their names appear on the Settings page with hidden values.
The one rule
The gateway’s
API_KEY_SECRETandJWT_SECRET_KEYmust never appear in this repository, in its configuration, or on any host that runs it.
The gateway mints and verifies inference grants. Cloud Agent verifies identity tokens with public JWKS keys and signs its own tokens with independent secrets. Any proposal to place either gateway signing secret here requires a design review.
Provisioning status
The original extraction provisioned staging on 2026-08-06 and production on 2026-08-07; the cutover record documents that history. Those dates do not show whether a current deployment is ready.
This repository publishes neutral releases. As a downstream operator, you own your configuration, provisioning status, rollout record, and recovery evidence. Keep secret material outside Git and back it up with the data it protects.
Local development tools
These values are read by local tooling and are not saved application settings.
Variable |
New? |
Value / who provisions |
|---|---|---|
|
NEW |
Optional output directory set by the developer invoking |
Control plane (cloud_agent)
Local and CI permission regression
AGENT_PERMISSION_TEST_IMAGE enables the opt-in Linux UID regression in
tests/external/test_shared_member_permissions.py. It names an already-pulled
Docker image with Python 3 and GNU find/stat, such as python:3.12-slim.
After pulling the image, run:
AGENT_PERMISSION_TEST_IMAGE=python:3.12-slim \
uv run pytest -q -m external tests/external/test_shared_member_permissions.py
The test uses disposable containers without network access or mounts and runs the production normalizer as separate member UIDs without capabilities. CI runs it before deployment; it needs no model API key or deployed-service setting.
Database
Variable |
New? |
Value / who provisions |
|---|---|---|
|
NEW |
Postgres DSN for a database owned exclusively by Cloud Agent. Do not share the gateway’s database. |
|
existing |
Supported control-plane Compose database initialization. User and database default to |
|
NEW |
Read by |
Automatic PR merge archiving
These environment values are imported when the Admin Settings row is first created. After that, edit the saved Publication settings. See the archiving runbook.
Variable |
Default |
Purpose |
|---|---|---|
|
|
Enable automatic archival after verifying that the current published PR merged. |
|
|
Seconds between GitHub merge checks; saved setting accepts 5–3600 seconds. |
API timeout, batch size, retry delay limit, and repeated integration-failure threshold are saved settings. The worker uses existing GitHub App credentials or the configured publication token, and never passes them to the sandbox.
pgAdmin database administration
The optional pgadmin Compose profile adds Admin → Database → Open pgAdmin.
The private service has no published port. Every request requires a current
agent.admin decision; pgAdmin also requires its own account login. Its
accounts, sessions, and saved connections persist in agent-pgadmin.
Variable |
New? |
Value / who provisions |
|---|---|---|
|
optional |
Private pgAdmin origin; use |
|
optional override |
Full public proxy URL ending in |
|
required for dashboard setup |
Set |
|
required for dashboard setup |
Private origin used only to initialize the bundled service, normally |
|
optional |
Public proxy URL for first-time setup, ending in |
|
optional |
Startup wrapper that waits for first-time dashboard setup before executing pgAdmin’s native entrypoint. Defaults to the bundled |
|
optional |
Read-only server-definition JSON. Defaults to the bundled |
|
optional |
pgAdmin Python boolean, default |
Gateway contracts
Variable |
New? |
Value / who provisions |
|---|---|---|
|
NEW |
Origin of the HybridInference gateway this deployment federates with, e.g. |
|
NEW |
Explicit URL of the gateway’s public key set, typically |
|
NEW |
Bearer credential for minting, renewing, revoking and reading usage for inference grants. Generate random 32 bytes (a new value saved in Admin → Settings must be at least 32 bytes; a shorter value saved or imported earlier still loads, and startup logs |
|
optional initial import |
Grant service route prefix, default |
|
optional; recommended before the first ledger-enabled deployment |
Nonsecret identity of the logical gateway installation. When set, usage snapshots pin this identity across URL, grant-prefix and credential migrations. Change it when switching installations, including at the same URL. Without it, the fingerprint uses |
|
Required for Gateway access |
Exact |
|
NEW |
Explicit consent-page URL, typically |
|
NEW |
Public URL of |
|
NEW |
Public web-app URL, including its base path, for example |
|
existing |
Gateway URL as seen from inside the sandbox — the relay address, not the public URL. Differs from |
Own secrets
Variable |
New? |
Value / who provisions |
|---|---|---|
|
NEW |
Optional dedicated key of at least 32 bytes for encrypted admin setting secrets. If absent, |
|
NEW |
Encrypts stored source-control credentials, per-user MCP credentials, and reusable cloud-environment variables/overrides under separate domain separators. Generate random 32 bytes, unique per environment. A new value saved in Admin → Settings must be at least 32 bytes; a shorter value saved or imported earlier still loads, and startup logs |
|
NEW |
Signs host/worker control tokens. Generate random 32 bytes, unique per environment and independent of gateway secrets. A new value saved in Admin → Settings must be at least 32 bytes; a shorter value saved earlier still loads, and startup logs |
|
NEW |
Signs the web session cookie. Generate random 32 bytes, unique per environment. |
|
NEW |
Server-owned policy for new root threads. Application default: |
|
NEW |
Authenticates the gateway alert relay on |
|
NEW |
Gateway user id every on-call job runs as. Its role decides which models the job’s grant may carry ( |
|
NEW |
Operator-owned JSON webhook for queue incidents, recovery and compute replacement events. Queue monitoring runs every minute, reports ten-minute-old runnable queue heads after two observations, retries delivery on the next pass and repeats unchanged incidents at most every 30 minutes. Delivery timeout: five seconds. Queue payloads contain event, deployment SHA and aggregate counts; compute events also contain Host identity and reason codes. Unset retains structured logs. Downstream distributions own deployment-workflow alerts. This URL never enters runners or sandboxes. |
|
NEW |
How long a member’s independently fenced live-process residency and short-lived inference grant survive between host heartbeats. Default 1800. Shared-live uses one residency per chat membership. This is credential/process ownership, not a second chat lifecycle clock: |
|
existing |
Optional machine-to-machine admin credential. An unset value opens no token access; admin sessions remain a separate authorization path. Use at least 32 random bytes; startup logs |
Sandbox deletion archives
Variable |
New? |
Value / who provisions |
|---|---|---|
|
NEW, optional initial import |
S3 bucket for stopped sandbox archives. Empty disables sandbox deletion. Saved as |
|
NEW, optional initial import |
AWS region for the archive bucket. Empty uses the standard SDK region configuration. |
|
NEW, optional initial import |
Optional HTTPS S3-compatible endpoint. It must support conditional |
|
NEW, optional initial import |
Object prefix for new archive requests, default |
|
NEW, optional deployment path |
Absolute path to an operator-owned private Compose override. |
Standard AWS SDK credentials |
NEW, deployment infrastructure |
Provision only on the main control plane through the standard SDK chain, preferably an IAM workload role. For environment or shared-profile credentials, set |
See sandbox archive storage for permissions, retention and verification requirements.
Behaviour switches read by the control plane
Variable |
New? |
Value / who provisions |
|---|---|---|
|
NEW |
Compose-level, read by |
|
NEW |
Where the edge listens; defaults |
|
existing |
Compose main-API binding, defaults |
|
existing |
Compose MCP-only listener binding, defaults |
|
existing |
Compose web-server binding, defaults |
|
existing |
Host-side nginx configuration mounted read-only by the edge service. Default |
|
existing |
Control-plane Compose network name, default |
|
NEW |
Defaults to on. Set to |
|
NEW |
Defaults to off. Set to |
|
NEW |
Default |
|
NEW |
Defaults to off. Set to |
|
NEW |
The npm registry the admin Settings page asks for each agent’s latest release, and checks a requested version against. Default |
|
NEW |
Exact commit this API serves. Required by the production control-plane Compose stack and exported by |
Source control
Variable |
New? |
Value / who provisions |
|---|---|---|
|
existing |
Identifies the GitHub App used for repository access. Setting it enables key loading; an unreadable key makes GitHub temporarily unavailable and is retried every 30 seconds. Without it, key settings are ignored. Configure the App’s callback/setup URLs for this deployment. See credentials. |
|
existing |
GitHub App OAuth client ID for users connecting their own GitHub account. |
|
existing |
GitHub App OAuth client secret. Store outside Git; rotate when changing the App’s audience. |
|
existing bootstrap |
Path inside the container to a read-only PEM key. Supported Compose mounts it at |
|
existing |
Host-side key file mounted read-only by control-plane Compose at |
|
existing initial import |
Inline PEM for a one-time import in deployments that already supply it. Compose does not forward this variable. Set or replace an inline key in Admin Settings, where it is encrypted and never returned to the browser. The mounted |
|
existing |
|
|
existing |
Defaults to |
|
existing |
GitLab.com OAuth application client ID for user connections. |
|
existing |
GitLab.com OAuth client secret; store outside Git. |
|
existing |
Public OAuth callback URI registered with GitLab. Must use HTTPS, except for localhost development, and anything else is refused when saved or imported; update the registration when the deployment URL changes. |
|
existing |
Comma-separated |
|
existing |
Commit identity for published work. Defaults: |
|
existing |
Optional fallback GitHub credential for publication without a GitHub App. Unset does not disable App-based publication. Long-lived publication credentials stay in the control plane and never enter sandboxes. |
|
existing |
Pull-request target branch, default |
MCP
Cloud Agent owns MCP discovery, per-user credentials, authorization and attempt fencing. The gateway serves inference and has no MCP role.
Users add remote URLs, imported mcpServers snippets or stdio launch specs.
Templates only prefill the connection form; environment variables neither add
servers nor grant access to them. Credentials live per user in
agent_mcp_connections, encrypted with
AGENT_SOURCE_CONTROL_ENCRYPTION_KEY. The settings below control
relay reachability and connection target policy.
Variable |
New? |
Value / who provisions |
|---|---|---|
~~ |
removed |
There is no deployment-wide MCP registry. It named a file of remote servers with their credentials, and every user who could create a job could use them under that one identity — the team-MCP shape this product does not offer. Per-user connections replace it; the relay, the proxy and the sandbox boundary stay. Listed here rather than deleted because an existing deployment has this set, and a variable that silently stops meaning anything is worse than one recorded as gone. |
|
host only |
The MCP relay as this host’s sandboxes reach it. The host compose derives it from the host’s own MCP tunnel ( |
|
NEW |
MCP-only SSH tunnel. Required suffixes: |
|
NEW |
The relay’s alias on both container networks. Defaults to |
|
NEW |
Runner-only SSH tunnel to the main API. Required suffixes: |
|
NEW |
That tunnel’s alias, on the runner network only. Defaults to |
|
optional override |
The public API base URL used to register and receive MCP OAuth callbacks, including the API prefix — for example |
|
optional legacy import |
Configure GitHub MCP browser sign-in in Admin Settings → MCP. Register a service-owned GitHub OAuth App or GitHub App with callback |
|
NEW |
Whether a user-added MCP connection may dial private and loopback addresses. Off by default: a remote connection that reaches loopback reaches this service, and a user who needs a private network should run a connector instead. Turning it on still refuses the ranges this platform never dials, so it cannot be used to reach the cloud metadata service. |
|
NEW |
Whether a user-added MCP connection may use |
|
NEW |
Not operator-set — listed so nobody provisions it. The name the runner publishes the relay endpoints under for the image-shipped wrappers, as one JSON object of |
|
NEW |
Not operator-set — listed so nobody provisions it. The name the generated sandbox runtime config references ( |
Sandbox settings from the admin pages
Host settings can be set from the admin pages instead of in each host’s
.env, including AGENT_TIMEOUT_S, AGENT_CHAT_IDLE_TIMEOUT_S,
AGENT_SOURCE_IMPORT_MAX_MIB, AGENT_SHARED_RESIDENT_CAPACITY,
AGENT_STORAGE_MIN_AVAILABLE_MIB,
AGENT_SANDBOX_CPU_MILLICORES, AGENT_SANDBOX_MEMORY_MIB, AGENT_SANDBOX_DISK_MIB,
AGENT_TERMINAL_IDLE_TTL_SECONDS, AGENT_TERMINAL_HARD_TTL_SECONDS and
AGENT_TERMINAL_MAX_SESSIONS. The Settings page sets a value for every
server (PUT /admin/agent/host-settings), and a server’s details on the
Servers page set one for that server alone
(PUT /admin/agent/runner-hosts/<host>/settings). For each setting the host
applies, highest first:
the value set for that server;
the value set for every server;
the built-in default.
The control plane sends complete effective values, including defaults, on each
heartbeat. Host environment values apply only before the first settings answer
and remain visible as diagnostic local values. Clearing a per-server value
uses the global value or built-in default; clearing a global value uses the
built-in default. The bounds an admin may set are in
cloud_agent_shared/host_settings.py and are typo guards, not capacity statements.
Container CPU, memory, and disk settings apply to new sandbox groups. Resuming a
chat or forking it preserves the group’s original limits; all members share
those resources. CPU is entered as vCPU in the admin pages and stored in
millicores (1 vCPU = 1000 millicores). Memory and disk accept MiB or GiB.
Defaults are 2 vCPU, 4096 MiB memory, and no per-container disk quota. Initial
environment values are AGENT_SANDBOX_CPU_MILLICORES=2000,
AGENT_SANDBOX_MEMORY_MIB=4096, and AGENT_SANDBOX_DISK_MIB=0.
A positive disk limit sets Docker’s --storage-opt size=<MiB>M on the writable
layer, including the workspace, caches, and installed files. It excludes the
read-only image and memory-backed temporary files. This requires Docker
overlay2 on an XFS backing filesystem mounted with project quotas (pquota
or prjquota). Docker refuses container creation when the storage backend does
not support the requested quota. Set the disk value to 0 to create groups with
no disk quota; resetting an override inherits the global value or default.
A positive disk limit is recommended on every server that runs the untrusted
profile (untrusted-kata-v1), whose sandboxes otherwise share one unbounded
disk. It is not required: a server at 0 still takes untrusted work, and the
control plane and runner log a warning and the Servers page shows No
container disk quota for it.
The runner’s existing storage attestation continues to measure the host
filesystem rather than each sandbox’s quota.
The same cards also manage workspace and terminal resource limits. These additional environment names supply initial local values only:
Variable |
New? |
Initial default / purpose |
|---|---|---|
|
NEW |
4 open terminals per shared workspace. |
|
NEW |
60 seconds for workspace commands and Git inspection. |
|
NEW |
2 MiB for one workspace file replacement. |
|
NEW |
512 KiB for one file preview. |
|
NEW |
2 MiB of workspace command output. |
|
NEW |
5,000 entries per directory listing. |
|
NEW |
1,024 MiB for one whole-workspace download, measured before compression. |
|
NEW |
2 MiB / 4,096 events retained for terminal reconnects. |
|
NEW |
32 MiB of output over one terminal’s lifetime. |
|
NEW |
64 KiB of input per terminal request. |
|
NEW |
4 concurrent viewers per terminal. |
Workspace commands and Git inspection are also bounded in number, by the broker rather than by a setting: at most 16 run at once on one host and 4 for one chat, on a thread pool of their own, and one more is refused with 429 rather than queued behind them.
The values ride the heartbeat answer, so a host applies a change within one
heartbeat (about 30 seconds) with no redeploy. Nothing running is interrupted:
a turn keeps the time limit it started with, an existing sandbox keeps the
session capacity and CPU, memory, and disk limits it was created with, and the sandbox manager’s terminal limits apply from its
next sweep. Each host reports what it applies and what its own .env gives,
and the page shows a value as applying until the host confirms it. A runner
that predates this (it does not advertise host-settings-v1) keeps its .env
values until it is redeployed, and the page says so.
Optional compute-resource management
These values belong only to the stable main control plane, not its MCP-only listener, any Host, or any sandbox. See the compute runbook for deployment profiles, verified SSH access and CloudLab endpoint setup.
Variable |
Meaning |
|---|---|
|
NEW |
|
Independent encryption key, at least 32 bytes, for stored compute connections and Admin-managed deployment profiles. The supported deployer generates and persists a random value in |
|
Absolute path inside the control-plane process to the operator-owned JSON deployment configuration. |
|
Absolute host directory mounted read-only at |
|
Editable first-time Server setup address for the service node. The supported deployer defaults to the node’s FQDN, falling back to its hostname; set an SSH address explicitly when using another deployment system or a different SSH alias. Saved setups take precedence. |
|
Optional first-time Server setup gateway address override. Defaults to the hostname of the saved Gateway API URL, such as |
|
Operator-verified HTTPS Portal API base. Leave unset to disable the CloudLab adapter. |
|
Optional. The HTTPS address new CloudLab nodes post their SSH host keys to, ending in |
Without this configuration the existing Host management remains available; resource creation requires configured credentials and deployment profiles.
Host agent (cloud_agent_host)
The stack’s own identity
Read by deploy/host/deploy.sh and deploy/host/sandbox_host.sh rather than by
compose, because they name the stack rather than configure it.
Variable |
New? |
Value / who provisions |
|---|---|---|
|
NEW |
Required host-stack project name, with no host default. It prefixes containers, images and networks; reusing a name lets a second deployment recreate the first stack. Give sibling environments distinct names, such as |
|
existing |
Host path for persisted enrollment state and readiness markers. Supported host Compose default: |
|
existing |
Compose shutdown windows, defaults |
|
NEW |
Comma-separated projects on this machine that are the other environment rather than a stray copy of this one. Setting it makes |
|
NEW |
Deployment-script wait for the two parallel rollout halves, default 900 seconds. The host uses the same bound while waiting for cold-start inference/MCP/control endpoints; the control deploy uses it while waiting for a fresh exact-SHA heartbeat and reverse sandbox manager probes. This is a rollout watchdog, not a chat/session timeout. |
Scheduling
Variable |
New? |
Notes |
|---|---|---|
|
NEW |
What |
|
existing |
Attempt lease horizon, default 120 seconds. Inference grant TTL is independent: |
|
existing |
Hard watchdog for one actively executing Agent CLI turn (default 3600 seconds). Hitting it fails/fences that turn; it does not mean the chat was idle and is not an alternate sandbox lifecycle timeout. Can be set from the admin pages, which win over this value; see Sandbox settings from the admin pages. |
|
existing |
Execution environment name redeemed by |
Sandbox Manager, runtime and reporting
Variable |
New? |
Notes |
|---|---|---|
|
existing |
How a runner dials its sandbox manager — normally the Compose service URL. The runner advertises |
|
NEW |
What the runner reports at enrolment when that differs from what it dials. It may be omitted in the integrated local stack, where the control plane and sandbox manager share a network. It is required by |
|
existing |
Pool-wide sandbox manager credential: provision the same value in the control plane and every host. Host records contain sandbox manager URLs, not separate credentials. A compromised host’s token therefore authenticates to other sandbox managers. Compare SHA-256 fingerprints during provisioning without displaying the secret; a mismatch causes sandbox manager 401 responses. Per-host sandbox manager credentials require a separate distribution design. |
|
existing |
Required reverse listener for standalone host deployments. Bind the actual stable control-plane Docker bridge gateway address, never loopback or |
|
existing |
Host-side target of that reverse forward, defaults |
|
existing |
Inference SSH tunnel. Required suffixes: |
|
existing |
Inference relay alias on runner and relay-discovery networks, default |
|
NEW |
Control-plane allowlist for sandbox manager addresses, comma-separated. A bare hostname admits that host on any scheme and port; a |
|
NEW |
Runner URL for registration, claims and heartbeats. Standalone Compose derives it from the main-API tunnel alias |
|
existing |
Worker-name prefix, default |
|
NEW |
Operator-selected base used by the trusted runner for source clone URLs; defaults to |
|
NEW |
Largest repository checkout a first turn may import into its sandbox, in MiB; default 1024, accepted 1–65536. Read by the runner, never supplied by a job. It counts the working tree and the fetched commit’s Git objects, and the single-commit pack stores most content a second time (incompressible files almost byte for byte), so a repository whose files total 600 MB can already exceed 1024. Past it the turn fails before the agent starts with |
|
existing |
Reaps one stale interactive PTY after no input/output activity (default 1800 seconds). A retained PTY uses the earlier terminal/chat limit and hands its last activity time to the supervisor, so cleanup does not start a second inactivity window. This setting never stops the chat container directly. Can be set from the admin pages, which win over this value; see Sandbox settings from the admin pages. |
|
existing |
Absolute lifetime guard for one PTY (default 8 hours), even if it remains active. This bounds a resource and credential exposure window; it is not a chat lifecycle state. Can be set from the admin pages, which win over this value; see Sandbox settings from the admin pages. |
|
existing |
How many interactive terminals the sandbox manager holds open at once across all chats (default 64, accepted 1–4096). Can be set from the admin pages, which win over this value; see Sandbox settings from the admin pages. |
|
NEW |
Expected daemon-side |
|
NEW |
Sandbox Manager path of containerd’s content store ( |
|
NEW |
Sandbox Manager-only directory for temporary stopped-container exports and immutable per-request deletion receipts. Code default |
|
NEW |
Number of credential-isolated member UID/private-tmpfs slots built into each new shared container. Default 8; accepted range 2–128. Fork reserves a free slot before any native mutation. This bounds resident chats in one group, while |
|
NEW |
The single product-level inactivity clock for a retained chat group. Defaults to 1800 seconds (30 minutes). Turn/file/Git completion and terminal activity advance the same group deadline; activity tokens prevent a mid-operation stop without stacking another full timeout. When the settled clock expires, the host issues |
|
NEW |
One-time host enrollment token (task G2). Minted at |
|
NEW |
Where the host credential is kept between restarts, written mode 0600 before any byte reaches it — this machine runs untrusted repository code next door. The code default is |
|
NEW |
Deployment-script-managed rolling fence, not operator-selected topology. The control plane requires the same exact SHA described above. |
|
NEW |
How many turns this host runs at once until an admin sets a number on the Servers page ( |
|
removed |
No longer read. Concurrency is the limit in |
|
NEW |
Free-text version for the pool listing. Operator-facing only — never a compatibility gate, and a missing value must not stop a host enrolling. |
|
NEW |
Whether a runner samples its Docker host’s CPU, memory and retained-container count for the admin Servers page. Defaults to enabled; |
|
NEW |
Which probe measures the filesystem holding the sandbox writable layers, shown as the Servers page’s Storage column. |
|
NEW |
Storage reserve for new sandboxes, in MiB; default 1024, accepted 1–1048576. Before a turn allocates a sandbox, the runner reads fresh available space through the attested storage probe above and declines the untouched turn when less than this remains; existing sandboxes can still resume. Unknown or unverified capacity is never treated as pressure, and the reserve is an admission guard, not a disk quota. Can be set from the admin pages, which win over this value; see Sandbox settings from the admin pages and host runtime settings. |
|
NEW |
Process log level, default |
|
NEW |
Process log format, default |
|
NEW |
Legacy static-experiment switch. With no explicit MODE, |
|
NEW |
JSON descriptor for the current profiling run: |
|
NEW |
Optional JSONL path for the finished measurements, which are logged as well. An external benchmark reads this file to aggregate a baseline. It is not an outbox and is not required for Postgres/Admin delivery: root producers send the same cached frozen record directly through their bounded delivery path. Log and JSONL publication still use one bounded background exporter, so a slow file or log handler never runs on the measured thread. The runner sends selected context to the sandbox manager in |
|
NEW |
Optional control-source override: |
|
NEW |
Which deployment’s control row this control-plane process commands; defaults to |
The control-plane, host and local Compose files pass optional profiling overrides through to their respective producers. An empty MODE reaches the shared parser and uses the same default as an unset variable: Admin control is available without setup. Normal schema migrations still apply. The deployment namespace is only needed when independent deployments share a database.
Collection starts Disabled on a fresh database. The Admin Enable/Disable button
controls the persisted run without editing configuration or restarting services.
There is no per-operation DB read when Disabled, but the existing background
policy sync still runs; this is not zero background I/O. A restart restores any
unexpired active experiment rather than creating one. config_version names the
immutable experiment and revision orders control changes. There is no renewal
lease. Disable does not rewrite the manifest expiry or discard in-flight records.
max_operations stays a per-producer-instance allowance — the Admin API reports
budget_scope=producer_instance and does not present it as a deployment-wide
cap. Different runtimes and execution profiles remain separate in Admin
distributions. A 2026-09-18 local rerun measured roughly 30 microseconds
of collector work per synthetic startup; this does not include a real
container, sandbox manager, network, or database.
Two benchmarks answer two different questions, and neither substitutes for the
other. scripts/bench_startup_profiling_overhead.py measures the collector’s
own cost on a synthetic attempt sequence — no claim, container, supervisor,
agent CLI or network — and its own output says so.
scripts/bench_operation_overhead.py drives the real control-plane routes over
a real socket against a real database and, with a container runtime available,
the real supervisor activation that produces each of the four startup paths. It
reports per-operation p50/p95, the delta against the uninstrumented cohort,
queue drop counts, the background cost, and the agreed
max(10 ms, baseline p95 × 1%) budget verdict. Read each script’s
what/not_what block before quoting either.
Physical servers and execution environments
AGENT_RUNNER_HOST identifies one enrolled execution environment. When two
stacks on the same physical server talk to one control plane, they need distinct
names, credentials, state roots and sandbox manager URLs. Do not rename or re-enroll an
existing environment to combine its retained chats with another one.
AGENT_PHYSICAL_HOST names the physical server for the admin inventory. Give
all environments on that server the same name, unique within the control plane.
deploy/host/sandbox_host.sh defaults it to the host’s short hostname; direct
Compose deployments must set it explicitly. Runners send it at registration and
on each heartbeat, stored as labels.physical_host. Older runners can omit it
without erasing existing metadata. No database migration is required.
The Servers table shows the executor matching the new-chat execution policy
on each physical server. Production Compose defaults to untrusted-kata-v1;
the local Compose overlay explicitly keeps trusted-v1. Legacy executors remain
in the API for retained-session ownership but are not offered for new chats.
Keep one placement executor per physical server; extra old deployments must be
retired after their retained conversations are no longer needed.
The global sandbox location selector is always visible, including with one server. It shows Automatic or the exact selected Host identity; grouping by a physical server never silently chooses a different executor. It applies to all new, unassigned sandbox groups. Retained groups keep their original Host, and the page shows their actual distribution. An unavailable saved selection stays visible with a recovery action and never silently falls back to another Host. Disabled servers are listed in a collapsed Unavailable servers section, not counted as choices. Slots are the displayed environment’s concurrency limit, not GPU counts or a sum of old and current deployments. Grouping never changes credentials, sandbox manager routing, or retained sandbox ownership. Server names are reported metadata; runtime suffixes are never guessed.
Set AGENT_PHYSICAL_HOST before host activation and use the coordinated release
phases in the rolling-update runbook.
Allow active turns to settle, complete the target host generation, and pass the
control-plane exact-SHA readiness fence before activating the API/web release.
Existing host credentials and retained sandboxes stay in place.
Sandbox isolation (host agent reads, sandbox inherits)
Variable |
New? |
Notes |
|---|---|---|
|
existing |
Defaults to |
|
existing |
Unset selects |
|
existing |
Required sandbox image, with no application default. Use distinct tags for sibling environments, such as |
|
NEW |
|
|
NEW, required |
Stable deployment namespace stamped onto retained containers and group bridges so sibling Compose stacks on one Docker daemon cannot discover, stop, or prune each other’s chats. It also names this deployment’s host nftables table and bridge-interface prefix, so a runner or sandbox manager without one refuses to start rather than sharing a default. The host stack uses its unique Compose project; the complete local stack derives it from |
|
existing |
Docker executable used by the host backend, default |
|
existing |
Sandbox user home, default |
|
existing |
Sandbox non-root identity, defaults |
|
existing |
Additional Docker arguments parsed with |
|
NEW (internal) |
Trusted runner-side path to the root-owned |
|
existing |
⚠️ Isolation escape hatch, and narrower than the row above: it only opens the |
|
NEW, required |
This deployment’s RFC1918 IPv4 pool; Docker allocates one |
|
NEW, required |
The one IPv4 resolver sandboxes may use, on TCP and UDP 53. It is the only exception to the protected ranges, so a site resolver on a private address, or on this host, works. Under runc Docker’s embedded resolver forwards to it; a Kata guest is given it in |
|
NEW |
Comma-separated IPv4 blocks sandboxes must not reach, on top of the built-in host, private, link-local/metadata, CGNAT, multicast and reserved ranges — an institution’s services on public addresses, for example. It can only add protection. The control plane reads the same variable when it checks an owner’s SSH server, so set it there to the hosts’ value. |
|
NEW |
The route-less network the two relays are discovered on; no sandbox joins it. Group lifecycle attaches the relays found here, under their aliases, to each group’s bridge. The host stack derives |
~~ |
removed |
Retired with the network tiers and the Squid proxy when sandboxes moved to one network (design). Nothing reads them: there is no tier to select, no proxy to point at and no open-network switch. Delete them from |
|
NEW (internal) |
Generated by the runtime adapter for the private OpenCode/Kilo stdio bridge. Operators must not set these. They select the already-authorized native session and fixed |
|
NEW (internal) |
Generated by the host plugin installer for pi, OpenCode, and Kilo. A JSON array of absolute paths to the selected plugins installed in the member’s private home; defaults to |
|
NEW (internal) |
Set by the sandbox manager on a repository environment action it starts in a member’s terminal: the scoped UID (31000–31063) the action runs as. Operators must not set it. |
|
NEW (internal) |
Passed by the sandbox manager to the wrapper that runs one repository environment script ( |
|
NEW (internal) |
Set only for the snapshot secret scan inside the sandbox ( |
Kata runtime qualification generation
The supported host deployment mounts /var/lib/freeinference/kata-runtime
read-only at /run/agent-kata-runtime in the runner. Its root-owned epoch.json
contains { "version": 1, "state": "ready", "generation": "<UUID>" } after a
verified install. setup_kata_runtime.sh holds a host-wide installation lock,
publishes updating before changing the runtime, links or configuration, and
publishes a new ready generation only after installation and verification
succeed. A failed update remains updating; rerun the installer to repair it.
All deployments on that host observe the same generation, including atomic
replacement of the marker. --check remains verification-only.
For an existing host, deployment verifies Kata and initializes only a missing marker through a short-lived, networkless runc container with access to this directory alone. It takes the same installation lock and never replaces an existing generation or clears an incomplete update. Missing epoch evidence requires full probes; malformed or updating evidence fails closed. In-place runtime/configuration updates must use the supported installer so every runner invalidates qualification before the first changed file.
Complete local stack database recovery
deploy/local/dev.sh stores the local Postgres password and matching
AGENT_DATABASE_URL in the gitignored deploy/local/.env.local. Back up that
file with any retained local Docker volume. If the file is missing while the
selected Compose project still owns agent-postgres, the script refuses to
generate a replacement password: PostgreSQL applies POSTGRES_PASSWORD only
when initializing an empty data directory, so a new value cannot unlock the
old volume.
up builds the required images first. It and start then bring up only
Postgres, wait for readiness, authenticate from the control-plane image using
the configured DSN, and run Alembic before starting the rest of the stack. On a
mismatch, restore the original
AGENT_POSTGRES_PASSWORD and AGENT_DATABASE_URL. The script never deletes the
volume or resets its password automatically.
Claude inference retry policy
The runtime adapter sets CLAUDE_CODE_MAX_RETRIES=15 and
CLAUDE_CODE_RETRY_WATCHDOG=0 for headless and resident Claude Code processes.
These are fixed launch settings, supplied by the runner rather than host
.env options. The pinned CLI retries transient inference requests with
exponential backoff and jitter within the current native turn. Disabling the
watchdog keeps the request retry count bounded; AGENT_TIMEOUT_S remains the
overall execution limit. Retry progress appears in chat, and exhaustion fails
the turn while preserving its native context. See
Runtime boundaries for delay limits
and rollout behavior.
Agent CLI version pins (image build)
The five default versions live in both
deploy/sandbox/Dockerfile.agent-sandbox and
apps/backend/cloud_agent_shared/shared_protocol.py. They are one reviewed protocol unit:
the image installs and labels them, while every runner and the control plane
use the matching constants to decide whether shared-sandbox-v1 may be
advertised or claimed. They are therefore source pins, not per-host .env
settings. Change both locations together and re-run the agent-loop conformance
suite when intentionally changing a default.
The Dockerfile still verifies every exact version against the installed binary
at build time. A dist-tag (latest) or range (^2.1) fails the build, so its
labels cannot claim a different binary than the image actually contains.
Updating an agent without a rebuild. Claude Code, Codex, OpenCode and pi can also be updated from the admin Settings page (Agent versions → Update to …). Every host then downloads that exact npm release into a local image beside the sandbox image, checks it with one real turn through this release’s driver against a model on loopback, and reports progress on its heartbeat; once every online host holds it, new chats use it and existing chats keep their version. Roll back returns new chats to the version they used before. Kilo is not offered: it runs a source build carrying a reviewed isolation patch. The defaults above stay what a host without a selection runs. See the design.
Variable |
New? |
Notes |
|---|---|---|
|
NEW |
Host side of the control-plane row above: the npm registry a host downloads updated agents from. Default |
|
NEW |
The Docker network the download step runs on. Default |
Each runtime takes a *_EXTRA_VERSIONS deployment setting — comma-separated,
blank by default — naming additional versions the same image carries beside
the reviewed default. That list is the menu: build time decides what the image
holds, and an admin picks which item new sandboxes run under Agent versions
on the admin Settings page (agent_runner_policy.runtime_versions). Blank leaves an image holding exactly
its defaults, which is where every deployment starts.
Every added version must be exact and its event stream must be compatible with
runtimes.py.
tests/external/test_runtime_stream_conformance.py is the gate for that claim,
and the way to point it at a candidate is to put the version on the
Dockerfile’s ARG line:
# deploy/sandbox/Dockerfile.agent-sandbox, beside the reviewed default
ARG CLAUDE_CODE_VERSION=2.1.220
ARG CLAUDE_CODE_EXTRA_VERSIONS=2.2.0
# then, from the checkout — no image build and no deployment needed
uv run pytest -m external tests/external/test_runtime_stream_conformance.py
The suite derives its parametrization from those ARG lines
(test_runtime_stream_conformance.py::_menu, _DOCKERFILE), not from an
environment variable, not from a built image and not from the deployed .env:
setting CLAUDE_CODE_EXTRA_VERSIONS=2.2.0 in the shell changes nothing about
what runs. It then fetches each version with npx at exactly that version and
drives it against a fake gateway, so the measurement is of the real binary — but
it is a measurement of the repository’s Dockerfile, so the candidate must be
on that ARG line first.
Interpret a passing conformance run within these limits:
claude-codeonly. This is the one runtime with a stream-conformance suite; the other four have contract fixtures and their adapters’ unit tests, and no equivalent gate. Adding a version for them is currently a review obligation rather than a test.It proves the parser, not the option. A pass means the version’s event shape is intelligible to our normalizer. It does not exercise the deployed image, the sandbox, or the dropdown.
Verify stream compatibility before offering a version to users.
Variable |
New? |
Notes |
|---|---|---|
|
NEW |
Extra |
|
NEW |
Extra |
|
NEW |
Extra |
|
NEW |
Extra |
|
NEW |
Extra |
Web (apps/frontend/)
Frontend configuration is public. Build-time routing settings enter the downloaded bundle and require a rebuild. Appearance settings are saved through Admin → Settings and read by the web server for each request. Deployment timestamps and release identity remain runtime environment metadata. These public values may not contain secrets.
Build-time: the image variant
A NEXT_PUBLIC_* variable is substituted into the bundle during next build.
Each one must therefore be an ARG in deploy/control-plane/Dockerfile.web
and a key under the web service’s build.args: a row here without both is a
variable an operator can set everywhere and change nothing. Every row names its
compose argument, and test_web_build_arguments_are_wired fails on a row that
has none. Changing one is a rebuild.
The base path and API prefix must match the reverse proxy. The supported
same-origin layouts use AGENT_WEB_API_URL=<AGENT_WEB_BASE_PATH>/api: /api
for the standalone edge, or /agents/api under /agents. The build rejects a
nonempty relative API value that disagrees with the base path. Absolute URLs
and an empty value are accepted for operator-defined routing; empty means API
requests start directly at /v1/.... The pairing tests run the actual Next.js
configuration.
Variable |
New? |
Compose build argument |
Notes |
|---|---|---|---|
|
|
Prefix the app is mounted under. |
|
|
|
Browser API base. For the shipped standalone edge, use |
|
|
|
Optional build identity, baked in by the deploy script. |
|
|
|
Optional build identity. |
Runtime: the deployment’s identity
The main control plane imports the first eight appearance variables below when
it initializes the settings document. Set them in its deployment environment
before the first start, then change them through Admin → Settings. The web
server reads the saved public appearance fields on every request through
GET /v1/agent/site-config; the same built image serves any deployment. A web
server without AGENT_CONTROL_PLANE_URL uses its environment for standalone
development. Deployment time remains web runtime metadata.
Variable |
New? |
Notes |
|---|---|---|
|
MOVED |
Product name in the header and the document title. Empty shows |
|
MOVED |
Optional organisation name beside the product name. Empty hides it. |
|
MOVED |
Link target for |
|
MOVED |
Status page, opened in a new tab from the header. Empty hides the link — an empty |
|
NEW |
User documentation for this deployment, opened in a new tab from Help in the account menu and from the My Settings page. Empty hides the link. Point it at the distribution’s own user guide; this repository’s developer documentation is for operators and contributors. |
|
NEW |
Where users reach the deployment’s operators: an http(s) support page, or |
|
MOVED |
|
|
MOVED |
|
|
NEW |
ISO timestamp with a timezone for the deployment footer. The control-plane deploy script sets the activation time in UTC and records it in |
The web service also receives the existing AGENT_DEPLOYMENT_SHA for the footer’s
version: the first seven commit characters are visible, with the full commit in
the tooltip. Both values are resolved per request, independently of the image’s
build date.
Saved URLs and booleans are validated before an admin update is committed.
The standalone environment fallback validates them when rendering.
The gateway’s /site-config endpoint is outside the contracts Cloud Agent
consumes; public saved appearance comes from this service’s own endpoint.
Set on the HybridInference side — not here
These are provisioned in the gateway’s environment. Listed so the contract has one place to be read from end to end.
Variable |
Task |
Notes |
|---|---|---|
|
C1 |
RS256 private key, PEM. Public half is served at |
|
C1 |
Optional. Public PEMs of previously-active signing keys, concatenated and published alongside the current key so tokens issued before a rotation still verify. Keep a key here for at least the maximum token lifetime plus the JWKS cache window, then drop it. Public halves only — a retiring key must not be able to sign. |
|
C3 |
The |
|
C2 |
Comma-separated exact-match redirect URIs. Matching is equality — no prefix, no suffix — so the agent web callback must be listed verbatim before F5. |
|
C5 |
Same value as the control plane’s copy above. |
Infrastructure (not environment variables)
Recorded here as what has to be decided, not as anyone’s actual answer.
Domains (DR4): one host for the web app and the control-plane API, the API under
/api— e.g.agents.example.organd a staging sibling. The exact names go in the overlay, and must matchIDENTITY_ALLOWED_REDIRECTSon the gateway exactly.Reaching the gateway: the control plane needs a route to
GATEWAY_BASE_URL. The sandbox reaches its two relays, the resolver inAGENT_EGRESS_DNS_SERVERand public IPv4 on TCP 80/443 — the same during setup and the agent run. Tool calls remain confined to the MCP-only listener. Inference keeps the existing gateway endpoint (AGENT_TUNNEL_REMOTE_PORTdefaults to 8080); a separate inference listener or credential-substitution relay is not a deployment prerequisite. Public replay of a leaked inference grant is an accepted risk, subject to its owner’s existing model permissions, quota and grant lifecycle. If a deployment fronts either relay through a tunnel, the port allocation is overlay detail.Sandbox image registry: choose where hosts pull
AGENT_SANDBOX_IMAGEbefore deploying them.
Adding a variable
Add a row here, in the right section, marked NEW.
Note who provisions it and whether it differs per environment.
If it is a secret, say where it is stored — never the value itself.
The PR checklist enforces this; a new variable without a row is a review stop.
Repository environments
Reusable repository environments are available by default. There is no feature switch or initial environment-variable import. A legacy repository_environments_enabled=false setting or AGENT_REPOSITORY_ENVIRONMENTS_ENABLED=false value is ignored. Settings, task selection and the workspace Environment tab are always offered. /config always reports environment limits.
A task with no configured variables or scripts uses the base environment without environment preparation. Configured workspaces still require an environment-capable runner and sandbox manager; owner authorization, encryption and execution isolation remain enforced.