Skip to content

Customize & deploy

Use this guide when you've run through the wizard, decided the template fits your use case, and now want to fork it into your own GitHub repo + ship a deployable image. If you're still evaluating, stay on the first-agent tutorial — you don't need any of this to run the agent locally.

Why this is a separate step

The setup wizard handles runtime customization — model, tools, persona, auth — without editing code. Everything below is structural: renaming the template throughout the codebase, bending the release pipeline to your repo, baking your fork's identity into the Docker image. Do it once per fork, not every time you tweak a setting.

The operator-fork contract

Fork identity and behavior are config/plugin-driven. If you're editing a core .py to customize, that's a missing seam — file it. A fork that adds (new files: config/, plugins/, new modules) syncs upstream with near-zero conflicts; one that edits upstream-owned core files fights every sync. The template exposes a seam for each common customization so you stay on the add-only side:

You want to customize…Seam (declare/add — don't edit core)
Advertised skills + card descriptiona2a.skills / a2a.description in config, or register_a2a_skill (#570)
Add / drop toolsregister_tools plugin / tools.disabled (no core edit)
Where a turn's memory lives (thread_id)register_thread_id_resolver plugin (#571)
Lock outbound callbacks / peer consultssecurity.callback_allowlist CIDRs in config (#572)
Outbound host allowlist for fetch_urlegress.allowed_hosts in config
Subagentsregister_subagent plugin / subagents.* config
Release pipelineRELEASE_ENABLED repo variable (no workflow edit)

The only file a clean fork still edits is the pyproject version line.

1. Fork the template on GitHub

bash
gh repo create protoLabsAI/my-agent \
    --template protoLabsAI/protoAgent \
    --public --clone

cd my-agent

Or: Use this template → Create a new repository from the browser. Pick a short slug (jon, echo-agent, product-director) — it ends up as the image name, metric prefix (AGENT_NAME), and Langfuse tag.

2. Set your name — don't rename protoagent

Do NOT sed-rename protoagent across the tree. The internal protoagent identifier is the logger namespace, the ~/.protoagent data dir, the PROTOAGENT_* env prefix, and the protoagent.plugin.yaml manifest name — renaming it rewrites ~120 files and conflicts on every upstream merge, for zero functional gain. The user-facing name is data, not code:

  • identity.name in config/langgraph-config.yaml (set by the wizard) — drives the console brand, window title, and agent card. A fork sets this once and the whole UI follows.
  • AGENT_NAME env — the short slug for the Prometheus metric prefix, Langfuse tag, and the <AGENT_NAME>_API_KEY auth header.

Leave the internal protoagent identifier alone. See Fork the template for the full no-rename rationale.

3. Un-freeze the release pipeline

The release workflows are opt-in via a repo variable (so a fork enables them without editing the workflow files — upstream changes then don't conflict on re-sync). There is no github.repository == guard to swap. Just set the variable on your fork:

bash
gh variable set RELEASE_ENABLED --body true   # in your fork's repo

prepare-release.yml and release.yml gate on if: vars.RELEASE_ENABLED == 'true'.

3b. Stay conformant with the fleet workspace-config standard

Every fleet-watched repo carries a shared baseline enforced by protoLabsAI/release-toolsverify-workspace-config, and checks.yml runs it on every PR — so a non-conformant fork goes red in CI. The rules that bite most often:

  • Owned runners. Every workflow runs-on: must be namespace-profile-protolabs-linux, never a GitHub-hosted label (ubuntu-*, macos-*, windows-*). Hosted runners burn metered minutes and may be disabled org-wide. Legit exceptions (cross-platform binaries, npm provenance) get an inline # workspace-config: allow-hosted-runner <reason>.
  • .beads/issues.jsonl committed, .beads/beads.db gitignored.
  • .automaker/settings.json committed; transient .automaker/ dirs (features/, checkpoints/, trajectory/) gitignored.

A fresh template clone already conforms. If you add or copy a workflow, keep it on the owned runner. To check (and scaffold) at any time:

bash
npx --yes -p @protolabsai/release-tools verify-workspace-config
npx --yes -p @protolabsai/release-tools init-workspace-config   # fills gaps

The release-notes step in release.yml also delegates to the shared protoLabsAI/release-tools@v1 Action (reads GATEWAY_API_KEY + DISCORD_RELEASE_WEBHOOK from CI secrets) rather than a per-fork script — nothing to copy or maintain.

4. Declare the agent card

Your card's advertised skills and description are your agent's identity — declare them in langgraph-config.yaml, don't edit server/a2a.py (#570):

yaml
a2a:
  description: "Acme Bot — turns support tickets into triaged, drafted replies."
  skills:
    - id: triage_ticket
      name: Triage Ticket
      description: Classify a support ticket and draft a reply.
      tags: [support]
      examples: ["triage ticket #1234"]
      # Optional structured output — enforced + emitted as a typed DataPart (#476):
      # result_mime: application/vnd.protolabs.triage-v1+json
      # output_schema: { type: object, properties: { ... }, required: [ ... ] }

A plugin can contribute card skills too, via register_a2a_skill(spec). The name and url already pick up identity.name, so the wizard-set name lands on the card. Omit the a2a: block and the template ships one free-text chat placeholder so a fresh clone stays callable.

5. (Optional) Add domain tools

See Starter tools for the full default set. To drop a core tool, list it in tools.disabled (config — no code edit). To add tools, ship a plugin (register_tools) — that's the no-fork path that survives upstream merges. Editing get_all_tools() in tools/lg_tools.py directly still works but is the legacy core-edit that conflicts on re-sync. Any tool the agent ends up with becomes a checkbox in the wizard and drawer automatically.

The memory tools are dropped automatically when middleware.knowledge: false; the scheduler tools when middleware.scheduler: false. See Schedule future work and Configuration for the toggles.

6. (Optional) Configure subagents

graph/subagents/config.py ships with one researcher. Register more SubagentConfig instances in SUBAGENT_REGISTRY and add matching fields in graph/config.py::LangGraphConfig — or ship a subagent as a plugin (register_subagent) with no core edit. The lead agent delegates via the task tool; the subagent delegation rules are built from the registry.

7. Build and ship the image

bash
docker build -t ghcr.io/my-org/my-agent:local .

# local test — mount the config volume so wizard completions persist
docker run --rm -p 7870:7870 \
    -e OPENAI_API_KEY="$OPENAI_API_KEY" \
    -v my-agent-config:/opt/protoagent/config \
    ghcr.io/my-org/my-agent:local

The Dockerfile declares VOLUME /opt/<agent>/config so even without -v the wizard writes persist across container runs on the same Docker host — they live in an anonymous volume. For production, use a named volume or host mount so you can back it up.

Once the local build is happy, merge a PR to trigger the release pipeline (Deploy via GHCR).

8. Delete TEMPLATE.md

Once the checklist is done, rm TEMPLATE.md and rewrite README.md to describe your specific agent — its purpose, its skills, its operators.

Canonical reference implementation

protoLabsAI/roxy is a fork built on this template, running in production as an autonomous ProtoMaker portfolio manager. When this guide doesn't cover a specific decision, Roxy is the filled-in example — worth a skim before you invent something new.

Part of the protoLabs autonomous development studio.