Skip to main content

Digitorn App Language Reference

Digitorn apps are declared in a single YAML file. The compiler parses that YAML into the AppDefinition Pydantic model and the daemon runs it.

There is one canonical schema (v2) with 8 top-level blocks. Every field has exactly one home; legacy flat YAMLs (execution:, modules: at the top level, ...) are still accepted by an alias pass that reshapes them to canonical before validation.

The optional schema_version: 2 declaration at the top of the file future-proofs against breaking changes.

The 8 blocks

BlockRequiredWhat it holdsDoc
app:YesIdentity - app_id, name, version, icon, color, tags, quick_prompts.App Configuration
runtime:No (defaults)Lifecycle - mode, entry_agent, max_turns, timeout, triggers, hooks, middleware, pipeline, context, workdir, default_channel.App Configuration, Triggers, Middleware, Tool Hooks, Context Management
agents:At least 1 in practiceList of agents. Each has id, role, brain, system_prompt, modules, pool, delegate_to.Agents, Multi-Agent
tools:NoWhat the agent can call: modules (dict), capabilities (grant / deny), channels (dict).Tools, Built-in Tools, MCP Servers, Channels, Security
security:NoRuntime boundaries: behavior, sandbox, credentials_schema.Behavior Engine, OS Sandbox, credentials.md
ui:NoPure display, never read by the daemon: theme, features, widgets, workspace (renderer), slash_commands, quick_prompts, greeting. (ui.preview is deprecated; use tools.modules.web_preview for iframe-preview attachments.)Client Manifest, Widgets, Workspace & Preview
dev:NoDeveloper affordances: skills, variables, include (fragmentation).Skills System, Bundle namespaces
flow:NoOptional declarative orchestration graph for multi-agent apps. Top-level since v2 because it changes how agents coordinate (explicit scenography vs implicit Agent() calls).Flows

The ui.workspace block (renderer) is a different concept from runtime.workdir (filesystem path). The schema renames the legacy execution.workspace to runtime.workdir to remove the ambiguity (see RuntimeBlock.workdir).

Quick example

This example uses a local Ollama model so no external credentials are required. Replace the brain block to use any other provider (provider: openai, provider: anthropic, provider: deepseek, etc.); see Agents for the full list.

app:
app_id: my-assistant
name: My Assistant

runtime:
mode: conversation

agents:
- id: assistant
role: assistant
brain:
provider: ollama
model: qwen25-7b-gpu:latest
backend: openai_compat
config:
base_url: http://localhost:11434/v1
api_key: ollama
system_prompt: |
You are a helpful assistant. Reply concisely.

tools:
modules:
memory:
config:
auto_remember: false
capabilities:
default_policy: auto
grant:
- module: memory
actions: [remember]

ui:
greeting: "Hello! How can I help?"

Deploy and chat with it:

digitorn start                                 # daemon if not running
digitorn app run my-assistant.yaml # deploy + arm triggers
digitorn dev chat my-assistant -m "Hi there!" # talk to it

Migration from the legacy flat shape

If your YAML has execution:, modules:, channels:, behavior:, ... at the top level, the compiler still accepts it via the alias pass. The bidirectional mirror means both shapes work at read time.

To rewrite the file in-place to the canonical 8-block form:

digitorn yaml migrate-v2 path/to/app.yaml

Field renames the migrator applies (no compat retention). Each row shows the legacy v1 path on the left and the v2 canonical path on the right:

Legacy v1Canonical v2
execution.workspaceruntime.workdir
execution.workspace_moderuntime.workdir_mode
execution.greetingui.greeting
execution.sandboxsecurity.sandbox
execution.credentials_schemasecurity.credentials_schema
dependencies.variablesdev.variables
dependencies.channelstools.channels
dependencies.credentialssecurity.credentials_schema
dependencies.payloadruntime.payload_schema

Top-level lifts (legacy → canonical home, fields keep their name):

Legacy top-level (v1)Canonical (v2)
modules:tools.modules
capabilities:tools.capabilities
channels:tools.channels
behavior:security.behavior
widgets:ui.widgets
workspace: (block at root)ui.workspace (renderer)
preview:ui.preview (deprecated, ignored at deploy - use tools.modules.web_preview)
theme:ui.theme
features:ui.features
slash_commands:ui.slash_commands
skills:dev.skills
variables:dev.variables
include:dev.include
middleware:runtime.middleware
pipeline:runtime.pipeline
flow: (in v1 was top-level too, but is now strictly canonical)flow: (top-level, NOT under runtime)

Everything that was under execution: (mode, triggers, hooks, max_turns, timeout, session_mode, direct_modules, tool_injection, default_channel, context, payload_schema, watchers, scheduler, ...) lifts to runtime: with the same name. security.sandboxsecurity.sandbox, security.credentials_schemasecurity.credentials_schema, ui.greetingui.greeting.

Documentation by topic

Getting started

Agents and tools

Memory and context

Runtime control

Security

UI and client

Operating and deploying

Modules

The daemon ships 23 agent-facing modules (under packages/digitorn/modules/):

agent_spawn, behavior, channels, context_builder, cron_native, database, dev_tools, filesystem, http, index, llm_provider, lsp, mcp, memory, preview, queue, rag, shell, vector, web, web_preview, widget, workspace.

packages/digitorn/modules/cron also ships but is a system-only scheduler with no @action surface; it is not listed in the module reference.

context_builder and llm_provider are auto-loaded; you never declare them under tools.modules. Per-module reference docs live under reference/modules/.

Architecture

The compiler walks the YAML once, validates against the eight Pydantic blocks, then bootstraps each declared module. Once running, every tool call goes through the AgentContext, hooks fire around it via HookRunner, and ContextBuilder decides what tool schemas reach the LLM (direct vs discovery vs compact).

Tool delivery - direct, compact, or discovery

The ContextBuilder module exposes meta-tools the agent can use to discover capabilities lazily:

  • direct - full tool schemas injected up front. Best for small apps (< ~30 tools).
  • compact_direct - tool names + 1-line descriptions, full schema on demand via get_tool.
  • discovery - only list_categories, browse_category, search_tools, get_tool, execute_tool injected. Agent walks the tree as needed. Scales to hundreds of tools.

The mode is auto-detected by the compiler based on tool count, or forced via runtime.tool_injection.

LLM compatibility

Three backends are supported (AgentBrain.backend: Literal["openai_compat", "anthropic", "github_copilot"] in, default openai_compat):

  • openai_compat - any OpenAI-compatible /v1 endpoint (OpenAI, DeepSeek, Groq, Mistral, Together, Ollama, vLLM, LM Studio, OpenRouter, Cerebras, Perplexity, Fireworks, xAI, Gemini, ...).
  • anthropic - Anthropic SDK (also accepts the claude-code API-key alias for Claude Code OAuth tokens, see llm_provider module).
  • github_copilot - uses your GitHub Copilot subscription.

Models that support native tool calling (OpenAI, Anthropic, DeepSeek, Groq, Mistral, Together) get tools via the API tools= parameter. Models that don't (Ollama, LM Studio, vLLM, small local models) get tool schemas injected into the system prompt; tool calls are parsed from the text output via a multi-format recovery parser.

CLI

The digitorn command is exposed by the digitorn PyPI package. All sub-commands are registered with Typer.

# First-run wizard + environment doctor
digitorn init
digitorn doctor

# Apps (validate, deploy, run, schema, list, undeploy, delete)
digitorn app validate <app.yaml>
digitorn app deploy <app.yaml>
digitorn app run <app.yaml> # equivalent to deploy --force, shows triggers
digitorn app schema <module_id>
digitorn app list
digitorn app undeploy <app_id>
digitorn app delete <app_id>

# Per-app encrypted secrets
digitorn secret set <app_id> <key> [value]
digitorn secret get <app_id> <key>
digitorn secret list <app_id>
digitorn secret delete <app_id> <key>

# YAML migration
digitorn yaml migrate-v2 <app.yaml>
digitorn yaml migrate-credentials <app.yaml>

# MCP servers
digitorn mcp install <server>
digitorn mcp list
digitorn mcp uninstall <server_id>

# Middleware
digitorn middleware list
digitorn middleware install <path>
digitorn middleware uninstall <middleware_id>

# Dev loop (test apps against the live daemon)
digitorn dev deploy <app.yaml> [--scope system|user]
digitorn dev chat <app_id> [-m "message"]
digitorn dev status <app_id>
digitorn dev history <app_id> <session_id>

# Daemon control
digitorn start [--host 127.0.0.1] [--port 8000] [--workers N] [--config config.yaml] [--app app.yaml]
digitorn supervise [...] # daemon under restart-on-crash supervisor
digitorn stop [--host 127.0.0.1] [--port 8000]
digitorn status [--host 127.0.0.1] [--port 8000]
digitorn version

# System service (systemd / launchd / Windows Service)
digitorn service install # register the daemon as an OS service
digitorn service start
digitorn service stop
digitorn service status
digitorn service logs
digitorn service uninstall

# Module catalog + credential vault + packages + hub
digitorn modules ...
digitorn credentials ...
digitorn requires ...
digitorn package ...
digitorn hub ...
digitorn install-local # pair this daemon to a central account
digitorn auth login|logout|whoami # CLI auth against the central service
digitorn db ...

digitorn with no arguments prints Typer help. For systemd / launchd / Windows service installation, see Production Deployment.