Skip to main content

Agent manifest

Each Covenant agent is a subdirectory of $COVENANT_HOME/agents/ containing an agent.toml manifest. The router walks agents/, picks up each subdirectory's agent.toml, and resolves agent.entry against that package directory. Flat *.toml files at the top of agents/are silently skipped. The manifest declares the agent's identity, runtime, package-relative executable path, required capabilities, resource budget, sandbox requirement, and optional settlement configuration.

Example

[agent]
id      = "research"
name    = "research"
version = "0.1.0"
runtime = "rust-bin"
entry   = "research"

[capabilities]
required = ["tool.web_search"]
optional = ["memory.write"]

[resources]
cpu_ms_per_task = 30000
memory_mb       = 512
disk_mb         = 100
network         = "outbound-https-only"

[sandbox]
required   = true
backend    = "linux-gvisor"
filesystem = "read-only-package"

[settlement]
budget_credits_per_hour = 1000
priority                = "normal"

Schema

[agent]

FieldTypeRequiredNotes
idstringyesStable identifier. ASCII [A-Za-z0-9_.-]+; @ is rejected at parse time. Used as the routing key and the audit-log matched_agent value. The daemon synthesises<id>@agent as the agent's AgentId.display for budget keying.
namestringyesDisplay name; appears in CLI listings.
versionstringyesSemVer recommended.
runtimeenumyesrust-bin, python3, node, or hermes. The first three exec entry as a subprocess; hermes delegates to a configured Hermes HTTP endpoint and ignores entry.
entrystringyesPath to the binary (for rust-bin) or the entry script (for python3 / node). Resolved relative to the manifest's parent directory unless absolute. Ignored when runtime = "hermes".

[capabilities]

FieldTypeDefaultNotes
requiredlist of action strings[]Every action in this list must be present in the issuer's active capability set or the dispatch is rejected.
optionallist of action strings[]Recorded for visibility but not enforced.

Action strings live in reserved namespaces: intent., memory., identity., tool., agent.. The daemon validates that required and optional actions sit in one of these namespaces.

[resources]

FieldTypeDefaultNotes
cpu_ms_per_tasku64 milliseconds30000CPU budget. The runtime preempts the process when the projection tick flags projected overshoot and kills it at the elapsed cap as the backstop.
memory_mbu64 MiB512Advisory today; enforced by sandboxed runtimes.
disk_mbu64 MiB100Advisory today.
networkenumoutbound-https-onlyoff, outbound-https-only, or full.

[sandbox]

FieldTypeDefaultNotes
requiredboolfalseWhen true, the manifest must name a sandbox-grade backend. Trusted-local subprocess execution is rejected.
backendenumtrusted-localtrusted-local or linux-gvisor. The runtime crate has a gVisor runner and the daemon supports the linux-gvisor backend; live Linux CI coverage runs on sandbox-runtime path PRs via gvisor-live.yml. Promoting that workflow to a required check and broadening sandbox policy enforcement remain planned.
filesystemenumread-only-packageread-only-package, ephemeral, or host. The field is parsed now and enforced by sandboxed runtimes.

[settlement]

FieldTypeDefaultNotes
budget_credits_per_houru640Soft cap; tolerated as 0 until budget and settlement enforcement are configured for the agent.
priorityenumnormallow, normal, high.

[hermes]

Optional block consulted when runtime = "hermes"; ignored otherwise. Hermes manages its tool allowlist server-side today, so both fields are documentary — they pin the contract the agent author expects, surface in operator listings, and act as the enforcement seam once Hermes exposes per-run controls.

FieldTypeDefaultNotes
tools_allowedlist of strings[]Tools the agent expects the run to invoke. Names match Hermes's tool-registry slugs (e.g. terminal, read_file, web). Operators can spot a manifest that over-asks before granting capabilities.
approval_policyenumoperator-promptHow the runner should handle Hermes approval.request events when no operator is online. operator-prompt blocks until an operator answers via the console; auto-deny short-circuits to a denied response; auto-once accepts a single approval and stops. Reserved — runtime enforcement lands once the Hermes runner learns to post /v1/runs/{id}/approval.

Runtime contract

At dispatch, the runtime spawns the agent according to runtime and entry:

runtime = "rust-bin"   →   exec entry directly
runtime = "python3"    →   exec python3 entry
runtime = "node"       →   exec node    entry
runtime = "hermes"     →   POST to a configured Hermes HTTP endpoint

The first three runtimes communicate over stdin/stdout. The agent reads exactly one JSON line from stdin:

{
  "id":         "uuid",
  "text":       "the user's intent",
  "issuer":     { "display": "user@local", "pubkey": "…" },
  "issued_at":  1714938000000,
  "priority":   "normal",
  "parent":     null
}

And writes exactly one JSON line to stdout:

{
  "text":    "…",
  "sources": ["…"]
}

Stderr output is captured by the daemon's tracing subsystem and surfaces in operator logs. The agent process must terminate within resources.cpu_ms_per_task; the runtime preempts the process via SIGTERM/grace/SIGKILL when the periodic projection tick observes that the process is on track to exceed the cap, and falls back to the wall-clock kill at the cap if preempt did not fire. Either path produces a dispatch error. Successful processes with malformed stdout are rejected as runtime failures, not accepted as successful dispatches. The current subprocess runner is trusted-local. If sandbox.required is true, it fails closed instead of silently running the agent without sandbox-grade isolation.

Validation rules

The manifest parser rejects manifests that:

  • omit or leave empty any of agent.id, agent.name, or agent.version;
  • omit or leave empty agent.entry when runtime is python3, node, or rust-bin (runtime = "hermes" ignores agent.entry entirely);
  • contain characters in agent.id outside ASCII [A-Za-z0-9_.-]+agent.id flows into the daemon's synthesised AgentId display and round-trips through that charset filter on every JSONL replay;
  • declare an agent.entry that is absolute or contains .. / root / drive components for a subprocess runtime — entries must be relative paths inside the agent package directory;
  • declare a required or optional capability action outside the reserved namespaces;
  • set sandbox.required = true while keeping backend = "trusted-local";
  • fail to parse as TOML.

Unknown top-level sections are tolerated for forward compatibility; subsequent releases may attach meaning to them.

Manifest discovery

On startup the daemon walks $COVENANT_HOME/agents/ and loads each subdirectory that contains an agent.toml; flat *.toml files at the top of agents/ and subdirectories without an agent.toml are silently skipped. The loader sorts the returned cards by agent.id so routing tie-breaking between equal-scoring agents is deterministic across hosts regardless of filesystem read order (APFS, ext4, and ntfs all return read_dir entries in different orders). Online registration is not supported; the daemon must be restarted after a new manifest is added. Existing manifests may be edited in place and are re-read on the next daemon start.

Related