Bring Your Own Agent

Farna Control can run an agent image you already own. You keep your agent; Farna Control provides the governed workspace, the identity it runs under, the schedule it runs on, and the audit trail around it. Your agent runs the same interface a first-party runner image satisfies, so nothing in your pipelines changes when you point a node at your own image.

Two ways in

An existing agent connects to Farna Control in one of two shapes, and neither requires an agent rewrite. If your agent runs from a terminal, Farna Control runs it inside a workspace it provisions. If your agent runs as a CI/CD job, Farna Control dispatches that job and waits for it.

  • Containerized or CLI agents: point a pipeline node at your container image or command (Docker, a Kubernetes pod, or an E2B sandbox). Farna Control provisions the workspace, passes the task context, and reads back the result.
  • CI/CD dispatch: Farna Control triggers the CI/CD system that already runs your automation (GitHub Actions, GitLab CI, CircleCI, Buildkite, Jenkins, TeamCity, Azure Pipelines, or a webhook), passes parameters, and awaits completion.

This page covers the first shape in full: running your image inside a Farna Control workspace, and the output contract that image must satisfy. For the second shape, see Connectors.

The agent interface

Your image must satisfy four things:

  1. It provides a POSIX sh. Farna Control’s file operations run through a shell, so a shell must exist in the image.
  2. Farna Control launches one process. Farna Control writes the prompt and the context files into the workspace, then starts one process from the command the agent declares and runs it to completion.
  3. Farna Control observes the exit code. A non-zero exit fails the node, and retries follow the node’s policy.
  4. It writes one JSON document. The process writes a JSON value to /home/user/output.json. A missing or unparseable file fails the node. Nothing else is read as output.

There is no protocol, no callback endpoint, no sidecar, and no configurable output path. The workspace holds no credential back to the Farna Control control plane. The output file and the exit code together cover reporting, so no second channel is needed.

The output file

The output file is required: a process that exits 0 without a parseable /home/user/output.json fails the node. The file must contain valid JSON, and no key inside it is required. A JSON object is the conventional shape, and Farna Control surfaces these keys when it finds them, so any agent that writes them gets uniform treatment in the run detail:

The output file
Key Type What Farna Control does with it
status string Surfaces the agent’s own verdict verbatim as agent_status. It never changes the node’s status.
outcome string Surfaces the agent’s own verdict verbatim as agent_outcome. It never changes the node’s status.
summary string Surfaced as the node’s result summary.
changed_files array of strings Surfaced on the node output for review.
pr_url string Surfaced as a delivered pull request (PR) URL, marked agent-reported and unverified.
Pipeline config
{
  "status": "success",
  "outcome": "opened-pr",
  "summary": "Updated the retry policy and opened a pull request.",
  "changed_files": ["backend/src/modulo/core/retry.py"],
  "pr_url": "https://github.com/your-org/your-repo/pull/123"
}

Anything beyond these keys is yours to model. Declare an output schema to make it machine-readable.

Declaring the command

A sandbox agent node runs the command on its agent. In the default llm mode a node needs a non-empty agent_commands list and a prompt; in script mode it needs a non-empty script_command. The graph validator rejects a node that is missing its command (SANDBOX_MISSING_COMMAND). The items in agent_commands are joined into one shell command at run time.

Output schemas and the strict and lenient modes

An agent or a pipeline node can declare an output schema. The schema is pinned to a version and validated against the payload your agent writes. You can also declare no schema, in which case the envelope is accepted as a free-form JSON object.

Validation runs in one of two modes, chosen by an operator for the instance or for one organization:

Output schemas and the strict and lenient modes
Mode What it enforces
lenient (default) Missing required fields still fail the node. Violations of the other constraints (types, enums, patterns, formats, bounds, and composition) are logged as warnings, and the payload passes.
strict Any violation fails the node. Farna Control attempts a repair pass before it gives up.

The mode is resolved in this order: the organization’s setting, then the instance-wide setting, then the MODULO_SCHEMA_VALIDATOR_MODE environment variable as a first-boot seed, then lenient.

On a schema failure the node fails, the raw output is retained for diagnosis, and the run carries a contract schema error (contract.schema).

Where the image runs

Every workspace image resolves through one channel: the environment profile bound to the pipeline. The profile’s image_ref names the image, and the profile’s provider_type decides where it runs. The image is org-owned on every tier, so you control the supply chain wherever the work lands. Creating the custom environment profile an image needs is a Team capability (on the Community plan the environment-profiles API answers HTTP 402); see Execution Environments.

Where the image runs
Tier provider_type Where your image comes from
Bundled Runner (Docker) runner_docker The deployment’s own Docker engine, behind the shipped filtered socket proxy.
Remote Docker runner_docker A Docker engine you own, reached through MODULO_DOCKER_HOST.
Kubernetes kubernetes Your cluster and your registry. The image never leaves your boundary.
E2B e2b A template in your own E2B account. The profile’s image_ref is the template ID.

On Kubernetes, the profile must declare image_ref, and the image must provide a POSIX shell because the dispatcher’s file operations run through the exec subresource. Attach image pull secrets to the workspace ServiceAccount if your image is private. The Kubernetes guide covers the profile, the ServiceAccount, and the least-privilege Role.

Pin your images by digest for a reproducible supply chain. Farna Control resolves whichever image_ref you set; digest pinning is advice, not something the platform enforces. Execution Environments describes each provider in full.

What Farna Control observes and what your agent reports

Farna Control separates what it observed from what your agent claimed. Every fact carries one of four provenance values:

What Farna Control observes and what your agent reports
Value Meaning
observed Farna Control saw it directly through a credential it holds.
derived Farna Control computed it from observed facts.
reported It arrived from an external surface, including your agent’s output file.
attested A governance event Farna Control recorded itself.

For a bring-your-own agent run:

  • The process exit code and the wall-clock time are observed.
  • The node’s terminal status is derived from the exit code.
  • Everything read from /home/user/output.json is reported. Your agent’s status, outcome, summary, changed_files, and pr_url are the agent’s claims; Farna Control did not measure them.
  • Farna Control’s own approvals, guardrail verdicts, and human-in-the-loop (HITL) decisions are attested.

A reported datum also carries a witnessed_via stamp naming the channel it arrived through.

The default is optimistic: Farna Control records the provenance and does not block a run because a fact was reported. There is no built-in refusal mechanism. If you want to enforce a claim, compose primitives you already have: fetch the truth with a connector query and let a gate compare it against what the agent said. An agent that reports truthfully meets no friction.

How failures and partial results surface

The exit code decides the node’s status, and there is no synthetic completion:

How failures and partial results surface
Situation Outcome
Non-zero exit The node fails. Retries and terminal classification follow the node’s policy, and the raw output streams are retained.
Exit 0, but output.json is missing, empty, or unparseable The node fails with sandbox.no_output_json. Farna Control never invents a completion.
Exit 0, but the output is a lost-session marker The node fails.
Exit 0, a valid output.json, no declared schema The node completes and the envelope is surfaced as it is.
Exit 0, a valid output.json, a schema violation The node fails per the strict and lenient modes above, and the content is retained.

A partial result, meaning a failed run that still wrote a readable output.json, is kept for diagnosis and surfaced with its provenance intact. It never turns a failed node into a completed one.

  • Agents: the atomic unit of pipeline work and its configuration fields
  • Execution Environments: every runtime provider and how to choose between them
  • Kubernetes: run workspaces as pods in your own cluster
  • Schemas: versioned input and output definitions that validate agent output
  • Secrets: how connector and model credentials are encrypted at rest
  • Troubleshooting: common setup issues and fixes