Skip to main content

Overview

An agent is two files: a SKILL.md that tells the agent who it is and what to do, and a TRIGGER.md that tells the platform how the agent connects to the world. Nothing else is needed — no Dockerfile, no deployment YAML, no server code.
my-agent/
├── SKILL.md      # agent instructions (natural language)
└── TRIGGER.md    # machine-readable config + deployment metadata
zombiectl install <template> scaffolds both. This page is the reference for authoring your own.

SKILL.md — agent instructions

SKILL.md is plain markdown. The body is handed to the agent as its system prompt on every event. The frontmatter at the top carries small authoring metadata.

Frontmatter

---
name: platform-ops-agent          # required, must match TRIGGER.md
description: Diagnoses platform health from fly.io and upstash, posts to Slack.
version: 0.1.0                     # required, semver
when_to_use: ...                   # optional
tags: [platform-ops, diagnostics]  # optional
author: usezombie                  # optional
model: claude-sonnet-4-6           # optional, hint
---
FieldRequiredPurpose
nameyesIdentity. Must equal TRIGGER.md name: (install rejects mismatch).
descriptionyesOne-line summary, ≤200 chars.
versionyesSemver string.
when_to_use, tags, author, modelnoPass-through metadata.
Unknown top-level keys are accepted silently — drop in vendor-specific extras (x-amp:, etc.) without breaking install.

Body

Plain markdown. The whole body becomes the agent’s system prompt.
  • State the role plainly. “You are a lead qualification agent.” First sentence.
  • List the event shape. What arrives on the webhook? Name the fields.
  • Give an explicit procedure. Numbered steps beat prose.
  • Name the tools it can invoke. Match the names to the tools: list in TRIGGER.md.
  • Call out hard no-go behaviors. “Never promise a demo time.” “Never post to #general.”
  • Keep it short. Every token is paid on every event. Aim for under 1,500 tokens.
SKILL.md
---
name: platform-ops-agent
description: Diagnoses platform health from fly.io and upstash, posts to Slack.
version: 0.1.0
---

You are Platform Ops Agent. You diagnose problems in a small production
platform that runs on fly.io (app hosting) and upstash (managed Redis).
You are strictly read-only against fly and upstash; your one write path
is to post a plain-text summary into one Slack channel.

When an operator chats with you, gather evidence in this order:
1. List fly apps in the org. Note any not in `started` state.
2. For each suspect app, fetch recent logs via the fly API.
3. Pull upstash redis stats — connection counts, slow log, memory.
4. Correlate fly errors with redis pressure and post a one-paragraph
   summary to Slack with the failing app, the leading hypothesis, and
   the evidence link.

Never deploy, scale, restart, or flush. Never quote a fix as certain —
always frame as "leading hypothesis" and let the human decide.

TRIGGER.md — machine-readable config

TRIGGER.md carries the runtime contract: trigger source, allowed tools, vault credential references, network allowlist, and budget caps. Everything that controls security, cost, or scheduling lives here. The body below the frontmatter is operator-facing prose (credential shapes, budget rationale, etc.) — the runtime does not parse it.

Shape

---
name: platform-ops-agent   # top level — must match SKILL.md `name:`

x-usezombie:                # all runtime config nests under this key
  triggers:                 # array, 1–8 entries; one agent can wake on many sources
    - type: api             # operator chats via /steer; events arrive as event_type: chat
  tools:
    - http_request
    - memory_recall
    - memory_store
    - cron_add
    - cron_list
    - cron_remove
  credentials:
    - fly
    - upstash
    - slack
  network:
    allow:
      - api.machines.dev
      - api.upstash.com
      - slack.com
  budget:
    daily_dollars: 1.00
    monthly_dollars: 8.00
---

# Operator commentary about credential shapes, budget reasoning,
# firewall behavior — readable by humans, ignored by the runtime.

Top level

FieldRequiredPurpose
nameyesIdentity. Must equal SKILL.md name:.
x-usezombieyesRuntime config block. Required subkeys below.
Top-level keys other than name/x-* are accepted silently. Runtime keys (tools, credentials, network, budget, trigger) must not appear at the top level — they belong inside x-usezombie:.

x-usezombie: subkeys

Validation under this block is rigid: an unknown subkey is rejected with a typo hint. All listed subkeys are required unless marked optional.
SubkeyRequiredPurpose
triggers[]yesArray of 1–8 trigger entries. The singular trigger: shape is rejected at install (ERR_ZOMBIE_INVALID_CONFIG: use "triggers:" (array)). Entries are unique on the (type, source) tuple; at most one cron entry.
triggers[].typeyesOne of webhook | cron | api. See the table below.
triggers[].sourceper typeRequired for webhook. Carries the upstream label (e.g. github, slack, linear) and selects the per-trigger URL …/v1/webhooks/{zombie_id}/{source} — see Webhooks.
triggers[].eventsoptionalPer-webhook event whitelist (1–16 entries, ≤64 chars each). Omit to accept every event the source delivers.
triggers[].signature.secret_refoptionalVault key whose value verifies webhook signatures (first-class providers).
toolsyesNon-empty list of tool names from the Tools catalogue.
credentialsoptionalVault key names the agent reads at event time.
network.allowyesOutbound hostname allowlist enforced by the sandbox.
budget.daily_dollarsyesRolling 24-hour dollar ceiling.
budget.monthly_dollarsoptionalCalendar-month dollar ceiling.

triggers[].type values

YAML valueWakes fromUse whenCompanion fields
webhookAn external HTTP POST to https://api.usezombie.com/v1/webhooks/{zombie_id}/{source} with a signed body.Most production agents — anything driven by a third-party event (GitHub, Slack, Linear, Jira, etc.).source (label, also selects the URL) + events (optional whitelist) + signature (HMAC config). See Webhooks.
cronA self-scheduled timer the agent sets via the cron_add tool.Periodic health checks, drift sweeps, scheduled cleanups.None at the trigger level — schedules live inside the reasoning loop. At most one cron entry per agent.
apiA zombiectl steer invocation or a POST /v1/.../zombies/{id}/messages API call.Steerable, operator-driven agents — manual investigation, on-demand workflows, the install-time smoke test.None.
An agent can declare up to eight triggers and wake on any of them — e.g. one webhook from GitHub plus a cron sweep every 30 minutes. Any agent can also be steered via zombiectl steer regardless of declared triggers — steer always works. The api type is for agents whose primary surface is steer (no inbound webhook).
Long-run continuations are not a trigger type. When a run exhausts its context, the runtime re-enqueues the same event with actor=continuation:<original_actor> automatically. You don’t declare this in TRIGGER.md; it happens regardless of which trigger type woke the original run. See Context lifecycle.

Tools

The tools: list is the explicit allowlist of tools the agent may invoke. A compromised or jailbroken agent cannot reach outside this list. 23 tools are user-callable today, grouped by category — network (http_request, web_search, web_fetch), memory (memory_store/recall/list/forget), cron (cron_add/list/remove/update/run/runs), agent orchestration (delegate, spawn, schedule), browser, git, file edits, and stateless utilities. See Tools catalogue for every tool with its purpose and use cases.

Credentials

Each entry under credentials: is a bare vault key name referencing a JSON-shaped secret in the workspace vault (e.g. github carrying {api_token, webhook_secret}, slack carrying {bot_token}). Names like github, slack, linear, jira are credential references — not tool names. The tool bridge resolves the named credential at http_request time and substitutes its fields into outbound calls; the agent sees the response, never the raw secret. Add and rotate via Credentials.

Network allowlist

network.allow: is enforced by the sandbox firewall: outbound HTTPS to any hostname not on the list is dropped before it leaves the sandbox. Layered on top of the tool allowlist as a belt-and-suspenders control.

Budgets

FieldPurpose
daily_dollarsRolling 24-hour dollar ceiling for this agent.
monthly_dollarsCalendar-month dollar ceiling.
A breach terminates the in-flight event cleanly and records a budget_breach entry. Subsequent events in a later billing window are unaffected. Workspace-wide budgets live in Billing.

Common authoring mistakes

SymptomCauseFix
RuntimeKeysOutsideBlock at installtools:, credentials:, etc. at the top of TRIGGER.mdIndent them under x-usezombie:.
UnknownRuntimeKey at installTypo in a runtime subkey (e.g. crendentials: instead of credentials:)Fix the spelling, or remove the key. Validation under x-usezombie: is strict.
name_mismatch at installSKILL.md and TRIGGER.md disagree on name:One identity per bundle. Make both top-level name: values equal.
MissingRequiredField for SKILL.mdname, description, or version missing from SKILL.md frontmatterAll three are required at the top level.

Validation

zombiectl install --from <dir> reads both files and posts them to the server, which runs the same parser the runtime uses. Errors are returned with the field at fault.
zombiectl install --from ./my-agent
✗ install rejected: name_mismatch
  SKILL.md name: platform-ops-agent
  TRIGGER.md name: platform-ops-zombe    ← typo

Next

Bundled templates

See the anatomy of the flagship platform-ops sample.

Webhooks

Authentication modes and approval gates in depth.