Hosted Agents
Hand Dobby a container image and Dobby runs it for you as a managed Cloud Run job — building, preflighting, deploying, and monitoring the agent so every run lands in the compliance dashboard by construction.
Who can use this
Managed Agents is available to every workspace: no allowlist, no request to a Dobby admin. Regulated orgs are blocked from unattended production runs (403 REGULATED_PRODUCTION_BLOCKED) and can use this path for pilot and evaluation work.
First: give the agent an LLM (BYOK)
Hosted runs are bring-your-own-key: connect a provider under Settings → Connections → AI Agents and the gateway injects that provider's key + model into each run. With no provider connected, a customer run has no LLM and returns 503 — there is no silent customer-facing default. Managed LLM credits are not generally available yet. Connect a provider before your first run.
The lifecycle
1 · Package
Build a linux/amd64 image on the Dobby base image — the dobby-collector is baked in, so runs are monitored by construction.
2 · Preflight
The server preflight checks that the tag exists and is under 2GB. A CRITICAL finding blocks the deploy (422 PREFLIGHT_CRITICAL); a tag that was never pushed answers 404 IMAGE_NOT_FOUND. It is not a security scan: build with --platform linux/amd64 and scan your own image before pushing.
3 · Deploy
Dobby deploys the image as a Cloud Run Job (not a server — it runs to completion and exits 0). It injects DOBBY_CONNECTOR_ID + DOBBY_BASE_URL and mounts the bearer as a secret.
4 · Invoke
POST …/hosted-agents/{agent_id}/invoke → 202 { execution_id: he_… }. A cron schedule can fire it too. Poll …/executions/{id} until terminal.
Ship from CI (GitHub Actions)
There is no Dobby GitHub App and no build-from-repo — Dobby deploys an image you have already pushed to its registry, and deploy takes a tag in that repo, never an external image URI. Any CI that can run docker works.
The registry push token lives about an hour, so it cannot be stored as a CI secret. Mint one per run instead: POST …/hosted-agents/{agent_id}/push-token accepts an sk_live_ API key in x-api-key. The key needs a write scope and a creator whose tenant role is developer or above — an issued key is capped down to its creator's live role, so a viewer's key gets 403.
name: deploy-dobby-agent
on:
push:
branches: [main]
env:
DOBBY_TENANT: tenant_xxxxxxxx-xxx # from the dashboard URL
DOBBY_AGENT: ha_xxxxxxxxxxxx # the agent id
jobs:
ship:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Mint a fresh registry push token
run: |
RESP=$(curl -sS --fail-with-body -X POST \
-H "x-api-key: ${{ secrets.DOBBY_API_KEY }}" \
"https://dobby-ai.com/api/v1/tenants/$DOBBY_TENANT/hosted-agents/$DOBBY_AGENT/push-token")
TOKEN=$(echo "$RESP" | jq -r .push_token)
REPO=$(echo "$RESP" | jq -r .repo_path)
test "$TOKEN" != "null" || { echo "no push token: $RESP"; exit 1; }
echo "::add-mask::$TOKEN"
echo "PUSH_TOKEN=$TOKEN" >> "$GITHUB_ENV"
echo "REPO=$REPO" >> "$GITHUB_ENV"
- name: Log in to the Dobby registry
run: |
echo "$PUSH_TOKEN" | docker login -u oauth2accesstoken \
--password-stdin "${REPO%%/*}"
- name: Build and push
run: |
docker buildx build --platform linux/amd64 \
-t "$REPO:${{ github.sha }}" --push .
- name: Deploy
run: |
curl -sS --fail-with-body -X POST \
-H "x-api-key: ${{ secrets.DOBBY_API_KEY }}" \
-H 'content-type: application/json' \
-d "{\"image_tag\":\"${{ github.sha }}\"}" \
"https://dobby-ai.com/api/v1/tenants/$DOBBY_TENANT/hosted-agents/$DOBBY_AGENT/deploy"Treat the minted token as a live credential: mask it, pass it to docker login over stdin rather than -p (so it stays out of the runner's process list), and never commit it or echo it into build output. Prefer pinning third-party actions by commit SHA in any job that handles it.
Where results appear
The baked-in collector streams telemetry into the shared workload_runs surface, where the compliance scan + verdict attach — the same dashboards a monitored (BYOA) agent feeds. Hosted agents list at /dashboard/hosted-agents. Caps enforced today: max 30 hosted agents per workspace and 2 concurrent runs (429 QUOTA_EXCEEDED). A concurrency refusal means "not yet": queue the run and retry when a slot frees. Usage is computed for transparency (billing_status: not_invoiced).