Set up an agent (operator)

Go from a running pond to “an agent runs on my code” — all over the API, no DB editing. You’ll register a credential harness model, then attach a worker pool, then run a swarm stage.

This guide uses raw curl to show the API. The pond CLI wraps all of it — pond credential set, pond harness from-preset, pond model create, pond pool enroll-code, pond run submit — and is the easier path day to day.

Everything here is gated by the operator credential: the service token (POND_SERVICE_TOKEN) or an Auth0 admin. Examples use the service token:

POND=http://localhost:8001
H="Authorization: Bearer $POND_SERVICE_TOKEN"

1. Register the model credential

The provider API key — sealed at rest, never returned by reads.

curl -sX POST $POND/v1/admin/credentials -H "$H" -H 'content-type: application/json' -d '{
  "name": "anthropic", "provider": "anthropic",
  "env": {"ANTHROPIC_BASE_URL": "https://api.anthropic.com"},
  "secrets": {"ANTHROPIC_API_KEY": "sk-…"}
}'

2. Register the harness (codex/claude/…)

How to launch the agent CLI. command_template is the argv (with {{MODEL}} substituted); files are config templates dropped into the sandbox $HOME ({{BROKER_URL}}/{{SECRET:NAME}} are filled on the worker — see harness-runtime-files).

curl -sX POST $POND/v1/admin/harnesses -H "$H" -H 'content-type: application/json' -d '{
  "key": "codex", "label": "Codex", "capability": "harness.codex",
  "command_template": ["codex", "-m", "{{MODEL}}", "exec", "-"],
  "files": [{"path": ".codex/config.toml", "content": "model=\"{{MODEL}}\"\nbase_url=\"{{BROKER_URL}}\""}]
}'

3. Register the model

Ties a model id to the credential (and optional pricing for cost telemetry):

curl -sX POST $POND/v1/admin/models -H "$H" -H 'content-type: application/json' -d '{
  "key": "sonnet", "label": "Sonnet", "provider": "anthropic",
  "model_id": "claude-sonnet-4-6", "credential_name": "anthropic",
  "input_cost_per_mtok": 3.0, "output_cost_per_mtok": 15.0
}'

4. Attach a worker pool

Mint a pairing code, pair an orchestrator, then mint an enrollment code for it and attach a worker. (Details + flags: worker-pool; how the pieces deploy: Concepts What you deploy.) The orchestrator and workers are separate processes from pond — they run wherever your compute is and dial pond/the orchestrator inbound; pond never dials out to them.

# a) pairing code → pair an orchestrator
curl -sX POST $POND/v1/admin/pool/pairing-codes -H "$H" -d '{}'        # → {code, expires_at}
#    on the orchestrator host:  cd swarm && python swarm.py pair   (uses the code)
#                               python swarm.py serve --bind 127.0.0.1:8080 --token POOL_SECRET

# b) find the paired orchestrator
curl -s $POND/v1/admin/pool/orchestrators -H "$H"                     # → [{orch_id, …}]

# c) enrollment code for that orchestrator → attach a worker
curl -sX POST $POND/v1/admin/pool/enrollment-codes -H "$H" -H 'content-type: application/json' -d '{
  "orch_id": "…", "suggested_name": "w1",
  "tags": ["pool:trusted"], "capabilities": ["harness.codex", "sandbox.docker"]
}'                                                                    # → {code, expires_at}
#    on the worker host:  python swarm.py worker --orchestrator http://orch:8080 \
#                           --enroll-code CODE --tags pool:trusted --capabilities harness.codex,sandbox.docker

curl -s $POND/v1/admin/pool/workers -H "$H"                           # → the worker, once it registers

Match the worker’s tags/capabilities to what your stage requests (the harness capability + a sandbox.* backend + the trust tier’s pool: tag).

5. Run an agent

Submit a run whose swarm stage names the harness/model and a sandbox profile:

{
  "projectId": "…",
  "sources": [{"label": "app", "kind": "git_https", "config": {"url": "https://github.com/acme/app"}}],
  "definition": {"stages": [{
    "key": "fix", "name": "Fix", "executor": {
      "kind": "swarm", "prompt": "Find and fix the failing test.",
      "sandbox": {"profile": "untrusted-code-write", "tier": "trusted"}
    }}]}
}

Track it with GET /v1/runs/{id} + …/logs, pull results from …/artifacts (see integrate), and watch it in your traces if OTel is on (see observability).

Managing config

GET/DELETE mirror the POSTs: …/admin/credentials (lists secret key names only, never values), …/admin/harnesses, …/admin/models, and …/admin/variables (non-secret env — base URLs, model knobs — injected alongside a credential). POSTs are upserts — re-applying the same config is idempotent.

Egress allowlist (…/admin/egress). A pool-controlled list of hosts a confined run may reach (exact host, or .example.com for a domain suffix), org

  • project scoped. When any rule exists for a run’s scope it’s authoritative at dispatch — the consumer’s run config can’t widen egress beyond it (the worker’s forward proxy still adds the safe-default package registries). Confined profiles run on an internal network whose only route out is that proxy, so a malicious agent can’t bypass it even with raw sockets.

Scope. Config defaults to org (shared across the deployment). Pass a project_id on a POST to create a project-scoped entry that shadows the org one for that project; GET/DELETE take ?project_id= to target it (resolution prefers the project entry, then falls back to org). So a shared org default plus per-project overrides is a project_id away.

See also: Author a workflow · Sandbox a run.