Writing a Spawnfile
A Spawnfile manifest is a YAML file named exactly Spawnfile (no extension) at the root of your agent or team project directory. It must be valid YAML 1.2 and UTF-8 encoded with no BOM.
This guide covers every field the manifest supports.
Required Fields
Section titled “Required Fields”Every manifest must declare these three fields:
spawnfile_version: "0.1" # string, not numberkind: agent # "agent" or "team"name: my-agent # non-empty string, no whitespacespawnfile_versionmust be a quoted string matching a published spec version. Currently the only valid value is"0.1".kindis eitheragent(a single agent, possibly with subagents) orteam(an organizational unit of multiple agents).nameidentifies the agent or team. It must not contain whitespace.
runtime
Section titled “runtime”The runtime field declares which runtime adapter should compile the manifest. It is required for agents.
Short form:
runtime: openclawLong form with options:
runtime: name: openclaw options: profile: defaultThe short form is equivalent to the long form with an empty options map. The options object is adapter-specific and varies by runtime.
For agents with subagents, the parent’s runtime is inherited. Subagents must not declare a different runtime than their parent.
The docs block declares portable markdown surfaces that define the agent’s identity, behavior, and operating context. All fields are optional.
docs: identity: IDENTITY.md soul: SOUL.md system: AGENTS.md memory: MEMORY.md heartbeat: HEARTBEAT.md extras: user: USER.md notes: docs/NOTES.mdEach path must resolve to a UTF-8 Markdown file within the project root. See the Agent Docs guide for what goes in each role.
Paths must use forward slashes and must not escape the project root via .. traversal. Symlinks are not followed during compilation.
skills
Section titled “skills”The skills list declares skill directories available to the agent. Each entry must have a ref pointing to a directory that contains a SKILL.md file.
skills: - ref: ./skills/web_search requires: mcp: - web_search - ref: ./skills/memory_store requires: mcp: - memory_storeThe optional requires.mcp list names MCP servers that the skill depends on. The compiler validates these names against the agent’s mcp_servers list and reports an error if any required server is missing.
See Skills and MCP for details.
mcp_servers
Section titled “mcp_servers”The mcp_servers list declares MCP (Model Context Protocol) servers available to the agent.
mcp_servers: - name: web_search transport: streamable_http url: https://search.mcp.example.com/mcp auth: secret: SEARCH_API_KEY - name: local_index transport: stdio command: node args: - ./tools/index-mcp.jsEach entry must have a unique name within its manifest scope. The transport field must be one of stdio, streamable_http, or sse.
Transport requirements:
stdiomust declarecommand. It may declareargsandenv.streamable_httpmust declareurl.ssemust declareurl.
The auth.secret value should be an environment variable name, not a literal credential.
execution
Section titled “execution”The execution block declares portable intent about how the agent should run. Compilers map these values to runtime-native configuration.
execution: model: primary: provider: anthropic name: claude-sonnet-4-5 auth: method: claude-code fallback: - provider: openai name: gpt-4o-mini auth: method: codex - provider: local name: qwen2.5:14b auth: method: none endpoint: compatibility: openai base_url: http://host.docker.internal:11434/v1 workspace: isolation: isolated # isolated | shared sandbox: mode: workspace # workspace | sandboxed | unrestrictedprimary.providerandprimary.nameare required ifexecution.modelis present.fallbackis an optional ordered list of fallback models.- Each model target (primary or fallback) may declare inline
authandendpoint.
Model Auth
Section titled “Model Auth”Each model target may declare an auth block with a method field. Supported methods in v0.1:
| Method | Description |
|---|---|
api_key | Uses an environment variable for the provider API key. Default for built-in providers. |
claude-code | Imports the local Claude Code CLI credential store. |
codex | Imports the local Codex CLI credential store. |
none | No auth required. Default for provider: local. |
For api_key auth on custom or local models, use auth.key to name the env var:
auth: method: api_key key: MY_CUSTOM_API_KEYCustom Endpoints
Section titled “Custom Endpoints”Models using provider: local or provider: custom must declare an endpoint block:
endpoint: compatibility: openai # openai | anthropic base_url: http://host.docker.internal:11434/v1The compatibility field tells the runtime which API format the endpoint speaks. The base_url is the endpoint URL. Built-in providers (anthropic, openai) must not declare endpoint.
Workspace
Section titled “Workspace”isolation: isolatedmeans the agent gets its own workspace.isolation: sharedmeans the agent shares a workspace with others.
Sandbox
Section titled “Sandbox”mode: workspacerestricts the agent to its workspace directory.mode: sandboxedapplies stricter containment (runtime-dependent).mode: unrestrictedgives the agent full filesystem access.
A flat key-value map of non-secret environment values. Values must be strings.
env: LOG_LEVEL: infosecrets
Section titled “secrets”A list of secret declarations. Each entry must have a name and a required flag.
secrets: - name: SEARCH_API_KEY required: true - name: MEMORY_API_KEY required: falseThe compiler warns when a required secret is not present in the execution environment used for compilation.
policy
Section titled “policy”The policy block controls how strictly the compiler enforces capability preservation. It is optional and defaults to permissive mode.
policy: mode: strict # strict | warn | permissive (default: permissive) on_degrade: error # error | warn | allow (default: allow)strict— the compiler fails on any capability it cannot verify or preserve.warn— the compiler emits a warning and may continue.permissive— the compiler continues but still records the outcome in the compile report.
on_degrade controls behavior when a capability is partially mapped but not fully equivalent:
error— compilation fails.warn— compilation continues with a warning.allow— compilation continues silently.
surfaces
Section titled “surfaces”The surfaces block declares external communication channels for the agent. Spawnfile v0.1 standardizes four surfaces: Discord, Telegram, WhatsApp, and Slack.
surfaces: discord: access: mode: allowlist users: - "987654321098765432" guilds: - "123456789012345678" channels: - "555555555555555555" bot_token_secret: DISCORD_BOT_TOKEN telegram: access: mode: allowlist users: - "123456789" chats: - "-1001234567890" bot_token_secret: TELEGRAM_BOT_TOKEN whatsapp: access: mode: allowlist users: - "15551234567" groups: slack: access: mode: allowlist users: - "U1234567890" channels: - "C1234567890" bot_token_secret: SLACK_BOT_TOKEN app_token_secret: SLACK_APP_TOKENDiscord Fields
Section titled “Discord Fields”| Field | Description |
|---|---|
bot_token_secret | Env var name for the Discord bot token. Defaults to DISCORD_BOT_TOKEN. |
access.mode | Access policy: pairing, allowlist, or open. |
access.users | Allowed Discord user IDs. |
access.guilds | Allowed Discord guild/server IDs. |
access.channels | Allowed Discord channel IDs. |
Telegram Fields
Section titled “Telegram Fields”| Field | Description |
|---|---|
bot_token_secret | Env var name for the Telegram bot token. Defaults to TELEGRAM_BOT_TOKEN. |
access.mode | Access policy: pairing, allowlist, or open. |
access.users | Allowed Telegram user IDs. |
access.chats | Allowed Telegram chat IDs. |
WhatsApp Fields
Section titled “WhatsApp Fields”| Field | Description |
|---|---|
access.mode | Access policy: pairing, allowlist, or open. |
access.users | Allowed WhatsApp user identifiers. |
access.groups | Allowed WhatsApp group identifiers. |
WhatsApp does not have a portable token secret field. QR/session auth is runtime-defined.
Slack Fields
Section titled “Slack Fields”| Field | Description |
|---|---|
bot_token_secret | Env var name for the Slack bot token. Defaults to SLACK_BOT_TOKEN. |
app_token_secret | Env var name for the Slack app-level socket token. Defaults to SLACK_APP_TOKEN. |
access.mode | Access policy: pairing, allowlist, or open. |
access.users | Allowed Slack user IDs. |
access.channels | Allowed Slack channel IDs. |
Slack requires both a bot token and an app-level socket token.
Access Rules
Section titled “Access Rules”All four surfaces follow the same access-mode pattern:
- If
access.modeis omitted and any allowlist identifiers are present, the effective mode isallowlist. - Allowlist identifiers are only valid with
allowlistmode. allowlistmust declare at least one identifier list.- Discord uses
users,guilds, andchannels. - Telegram uses
usersandchats. - WhatsApp uses
usersandgroups. - Slack uses
usersandchannels. - If
accessis omitted entirely, the effective behavior is runtime-defined and not currently portable. Projects that need predictable cross-runtime behavior should declareaccess.modeexplicitly.
Runtime Support
Section titled “Runtime Support”Not all runtimes support all access shapes for every surface:
Discord:
openclawsupportspairing,allowlist, andopen.picoclawsupportsopenand user allowlists.tinyclawsupportspairingonly (DM-oriented).
Telegram:
openclawsupportspairing,allowlist, andopen.picoclawsupportsopenand user allowlists.tinyclawsupportspairingonly.
WhatsApp:
openclawsupportspairing,allowlist, andopen.picoclawsupportsopenand user allowlists.tinyclawsupportspairingonly.
Slack:
openclawsupportspairing,allowlist, andopen.picoclawsupportsopenand user allowlists.tinyclawdoes not support Slack.
The compiler validates the declared surface against the selected runtime and fails early on unsupported combinations.
Constraints
Section titled “Constraints”- Only agent manifests may declare
surfaces. Team manifests must not. - Subagents do not inherit parent
surfaces. - Surface auth secrets (like
DISCORD_BOT_TOKEN,TELEGRAM_BOT_TOKEN,SLACK_BOT_TOKEN) participate in the same auth profile and env validation path as model auth.
subagents
Section titled “subagents”The subagents list declares helper agents owned by the parent agent. Subagents are internal helpers, not team members.
subagents: - id: researcher ref: ./subagents/researcher - id: critic ref: ./subagents/criticEach subagent must have a unique id and a ref pointing to an agent source project (a directory with its own Spawnfile).
Subagents inherit the parent’s runtime. They do not inherit docs, skills, MCP servers, env, or secrets — each subagent is a self-contained project.
For execution, the parent’s execution is deep-merged with the subagent’s local execution: objects merge recursively, scalars replace, arrays replace wholesale.
Environment Variable Substitution
Section titled “Environment Variable Substitution”String values in a manifest may contain ${VAR} or ${VAR:-default} references that are resolved at manifest load time before schema validation.
execution: model: primary: provider: ${PROVIDER:-anthropic} name: ${MODEL:-claude-sonnet-4-5}
mcp_servers: - name: web_search transport: streamable_http url: ${SEARCH_MCP_URL}If a referenced variable is not set and has no default, the compiler fails. Substitution is not recursive — a resolved value containing ${...} is treated as literal.
The secrets[*].name and auth.secret fields are not substituted. They reference environment variable names, not values.
Metadata
Section titled “Metadata”Optional informational fields for project identity:
description: "Research analyst agent"author: noopolislicense: MITrepository: https://github.com/noopolis/analyst-agentThese are passed through to the compile report but do not affect compilation logic.
Complete Example
Section titled “Complete Example”Here is a full agent manifest showing all major sections:
spawnfile_version: "0.1"kind: agentname: analyst
runtime: openclaw
execution: model: primary: provider: anthropic name: claude-sonnet-4-5 auth: method: claude-code fallback: - provider: openai name: gpt-4o-mini auth: method: codex workspace: isolation: isolated sandbox: mode: workspace
docs: identity: IDENTITY.md soul: SOUL.md system: AGENTS.md memory: MEMORY.md heartbeat: HEARTBEAT.md
skills: - ref: ./skills/web_search requires: mcp: - web_search
mcp_servers: - name: web_search transport: streamable_http url: https://search.mcp.example.com/mcp auth: secret: SEARCH_API_KEY
surfaces: discord: access: users: - "987654321098765432" bot_token_secret: DISCORD_BOT_TOKEN telegram: access: users: - "123456789" bot_token_secret: TELEGRAM_BOT_TOKEN
secrets: - name: SEARCH_API_KEY required: true
policy: mode: warn on_degrade: warn