An executor is an operational unit whose arguments, effects, capabilities, and isolation profile are declared in a signed manifest. The system keeps shipped, generated, and imported executor provenance separate; the generated catalog supplies current counts, domains, and status without duplicating them on this page.

← Documentation index Architecture guide › executor

Metnos

executor — how it is made
Introductory guide

Ask Metnos with a request like this example: “Find every PDF in Documents that contains the word contract.” Metnos may compose several executors: one finds the files, another reads their contents, and a third filters the results. The user describes the outcome and does not need to know executor names.

Contents

  1. What an executor is (in thirty seconds)
  2. Anatomy: manifest, signature, and implementation
  3. The manifest: the calling card
  4. The fence: what it can and cannot do
  5. An executor's life
  6. Four origins: handwritten, generated, imported, builtin
  7. Four concrete examples
  8. Going deeper

1. What an executor is (in thirty seconds)

An executor is a small specialised operational unit that does one thing only: reads mail, finds a file, obtains the time, or sends a message. Subprocess executors have their own folders; in-process executors retain the same logical contract.

Think of a toolbox. Every tool is simple and recognisable: the screwdriver tightens screws, the hammer drives nails. Nobody would ask the screwdriver to drive a nail. In Metnos it is the same: every executor is a tool with a clear job. When the user asks for something, the planner picks the right tool and uses it.

arguments {tz: "Europe/Rome"} get_now one thing only: returns date and time manifest signature result 2026-05-06 16:45 what I ask the tool that runs what I get back
An executor is a box with a precise job, an inbound contract, and an outbound contract.

Three things define it:

Why executors are small

The more tasks a tool combines, the harder it becomes to verify its contract, authority, and effects. Separating file deletion from mail delivery makes every step legible and controllable. When Metnos composes several executors, the plan and each execution retain their own controls.

2. Anatomy: manifest, signature, and implementation

Executors in the distributed catalog normally use a flat folder containing four files. The contract does not impose that number: a manifest may list multiple code files, and in-process executors keep their signed contracts separate from their implementations.

get_processes/ manifest.toml the calling card: name, what it does, arguments, examples, output schema manifest.toml.sig the cryptographic signature of the calling card: it proves who wrote it get_processes.py the actual code: an invoke(args) function that performs the task manifest.lang_state.json translation state (multilingual): only when descriptions exist in more than one language common layout of the distributed catalog
The layout used by distributed executors: manifest, signature, entry point, and translation state. The manifest remains authoritative for the signed code files.

For an active subprocess executor, manifest, signature, and code form the core. The code does the work, the manifest lets the planner evaluate its contract, and cryptographic verification detects changes made after signing.

FileWhat it holdsWho reads it
manifest.tomlName, description, arguments, examples, output schema, declared capabilities, and execution policythe planner (to choose), the loader (to load)
manifest.toml.sigEd25519 signature of the manifest bytes; the manifest also carries the code digestthe loader during catalog admission
<entrypoint>.pyThe subprocess entry point; additional files may be declared in [code].filesthe runtime when the executor is invoked
manifest.lang_state.jsonPer-language description fingerprintsthe tools that keep translations aligned

The loader does not infer a contract from folder shape. It reads the manifest, validates the standard, signature, and code digest, checks the entry point, and only then admits the executor. Contracts for in-process executors live under runtime/builtin_executor_contracts/ and undergo the same cryptographic verification.

3. The manifest: the calling card

The manifest is a TOML file. You open it with a text editor and you can make sense of it even without being a programmer. It declares everything the planner needs to know: the executor's name, what it does, which arguments it takes, what the result looks like, a few examples for orientation.

name = "get_now" version = "0.1.0" affinity = ["time", "date", "now", "ora"] [description] en = "Returns the current date and time." [args.properties.tz] type = "string" default = "UTC" [output] schema_inline = "{ ok: bool,... }" [code] files = ["get_now.py"] digest = "sha256:e6e609f6..." [[capabilities]] name = "time:read" identity keywords helps the planner find this tool what it does a clear sentence, translated per language arguments type, default, description result shape what comes out of the tool, field by field code + fingerprint the digest changes if the code is touched permissions what it may touch
A real manifest, simplified. Every block plays a clear role: identity, keywords for retrieval, description, arguments, result shape, code with fingerprint, permissions.

What deserves a second look

Three things stand out, because the whole system rests on them.

The code fingerprint (digest): a cryptographic fingerprint computed over the bytes of the .py file. If anyone modifies even a single comma in the code without recomputing the fingerprint, the loader rejects the executor. The manifest and the code are bound like a certificate and the document it certifies.

The result shape (output.schema_inline): declares field by field what the executor returns. It serves whoever composes chains of executors (the planner does not fly blind: it reads the schema and knows what to expect at the next step) and whoever auto-generates code.

For facts that must not be softened by generated prose, an executor may also return an authoritative_presentation with a closed semantic scope. Exact counts and duplicate groups are examples. The runtime uses those fragments only when every productive step in the turn is covered; otherwise it keeps the ordinary finalizer. A display limit therefore remains distinct from the work performed: a fragment may claim a complete scan only when the executor also attests that its source was complete. A fragment suppresses the ordinary truncation notice only when this all-or-nothing composition succeeds for the whole turn; a covered step cannot hide a limit in an otherwise uncovered pipeline.

Identifiers may also carry manifest-declared source context. An array can map entries[*].uid through from_entries_key, while scalar properties such as account and source folder can declare a conditional from_entries_required. The projection accepts those values only when every source entry agrees. A direct call must provide the same applicable context explicitly, and the invocation choke-point rejects it before any side effect if that context is absent.

The permissions (capabilities): the manifest does not decide what the executor may do. The manifest declares what it would need in order to function; the system then decides whether to grant those permissions and under which constraints. See sandbox.

[[capabilities]]
name = "provider:access"
hint = ["google-workspace"]
when = { arg = "client", values = ["google_workspace"] }

For a remote backend, the when clause narrows the declaration to one invocation. Only when the final client value matches does the same binding enable network access, the read-write credential home, and server placement. An arbitrary value in client grants nothing.

One execution policy

Every invocation passes through a central runtime scheduler. If the [execution] section is missing, incomplete, or invalid, the loader always selects serial execution. Parallelism is therefore an opt-in property of the signed contract, admitted only after repeated equivalence tests between serial and concurrent execution.

ClassPortable meaningBehaviour
0No threadStays on the caller thread.
1ModerateUses a small share of the central pool.
2HighRequests more concurrency within resource limits.
3MaximumStill bounded by hardware, backend, and global ceilings.

At startup the runtime observes visible CPUs and the operational max_workers setting and establishes one instance ceiling. A signed class is a reduction of that ceiling, not a fixed thread count: an executor may only lower its assignment according to available work or its I/O profile. In one place the runtime applies bounded queues, backpressure, per-resource pools, and metrics without changing arguments, results, causal order, permissions, or success criteria. A non-read-only executor may declare a positive class, but it must also provide a resolvable concurrency identity; invocations sharing that identity remain serial.

LLM-backed executors follow the same rule. At startup, framework and hardware determine the LLM resource ceiling: a single-slot backend degrades to class 0, whereas a batching backend can admit more requests. Executors do not hide independent competing concurrency pools.

Recursive filesystem searches use one shared visitor: directories form a dynamic queue, idle workers take the next branch, and results are reordered before a limit is applied. Exact duplicate search, for example, filters by size and samples before computing complete SHA-256 digests for the remaining candidates; its display limit does not reduce the compared source.

Why the contract uses TOML

A manifest must remain readable, commentable, and editable during review. TOML preserves that legibility while retaining a structure that the loader can validate rigorously.

4. The fence: what it can and cannot do

The manifest declares the maximum capabilities requested by the executor. Before invocation, the runtime checks the concrete arguments, resolves only the required resources, and applies identity, vaglio, and policy controls. The sandbox is an additional containment layer; it grants no authority and does not replace those controls.

LayerCurrent server behaviour
Contractcapabilities, placement, platforms, and execution policy come from the signed manifest.
Application controlsArguments may narrow a signed scope but never widen it; identity, consent, and destination are checked before the subprocess starts.
Bubblewrap activeCode and runtime are read-only, /tmp is private, data resources are mounted with the required access, and the network is separated when no capability needs it.
Bubblewrap absent or disabledApplication checks remain, but the command runs directly and must not be described as operating-system isolation.

Network access is currently binary: when required, the process inherits the host network; there is no domain allowlist. Some system roots, including /etc, are visible read-only. See the sandbox guide for mounts, exceptions, downgrades, and the differences among Linux, Windows, and macOS.

5. An executor's life

The lifecycle field separates candidates from executors available to the planner. It is not a mandatory sequence for every origin: reviewed, distributed executors may begin as active, while Synt candidates pass through preparation and admission states.

StateMeaningIn pool?
proposedTriage metadata without code files; the loader rejects it if it binds code.No; visible only to work and audit surfaces.
synthesizedA candidate with code. When signature verification is enabled, it must already pass signature, digest, and entry-point checks, but the composer still cannot use it.No; available to the Synt path.
activeAn admitted contract visible to the planner.Yes, unless disabled or dormant because prerequisites are missing.
deprecatedExcluded from new compositions; retained as a compact index for diagnosis and replacement.No.
archivedExcluded from the operational catalog; its state remains in the lifecycle registry.No.

Automatic inactivity ageing applies only to Synt-generated executors: after 30 unused days they become deprecated, and after another 14 days in that state they become archived. Handcrafted executors, skill imports, and protected names are not retired merely because they are rarely used. The thresholds are configurable and restoration is explicit.

6. Four origins: handwritten, generated, imported, builtin

All executors expose the same logical contract, but they originate in four different ways. The distinction is not cosmetic: it changes who writes them, where they live, and which provenance is recorded.

Handwritten

The author crafts them with care. They form the stable core, the seed from which everything else grows.

Folder: executors/ in the installation.

Examples: get_now, find_files, read_messages, send_messages.

Tight, robust, reviewed many times.

Generated on the fly

When the catalog does not cover a request, the Synt composes a new executor in five steps (name, contract, tests, description, code).

Folder: ~/.local/share/metnos/executors/

Example: a narrow candidate prepared through a governed request when the catalog lacks a required capability.

Kept apart: they can never overshadow handwritten seeds.

Imported from an external skill

A public skill describes how to use a third-party service. Parsing and deterministic closed-vocabulary mapping turn it into one or more Metnos executors.

Folder: ~/.local/share/metnos/executors/skills/

Examples: read_events, set_events, delete_events (from a calendar skill).

Same checks as a generated one: no special treatment for coming from outside.

Builtin

Internal services executed in process. Their implementations live in the runtime, separately from their signed contracts.

Contracts: runtime/builtin_executor_contracts/

Examples: admin, create_tasks, list_skills, describe_images.

System services, not regular tools.

Precedence of the curated catalog

A generated or imported executor cannot use the name of a handwritten executor. Admission rejects the collision. If it is discovered while the catalog loads, the curated executor remains active and the synthesized folder is moved to a recoverable temporary area. Generation or a third-party skill therefore cannot silently replace the catalog's curated core.

Why the imported origin exists

A third-party library typically has its own textual documentation explaining how to use it: «to list the calendar, call gws calendar list; to create an event, use --summary and --start». The agentskills.io standard encodes this documentation in a precise format (a Markdown file with a structured front matter). Metnos's importer reads that format, translates it into the system's closed vocabulary, and generates the executor folder as if it had been handwritten.

The upside: any service already documented as a skill (Google calendar, mail, file storage,...) can be brought into Metnos without rewriting from scratch. The downside: you have to trust whoever wrote the skill (and its helper scripts). For this reason the importer does not install anything in executors/: imported executors live in the separate data folder, under watch, and still pass through the vetting before every call.

One operational detail: an executor that requires credentials remains dormant and is excluded from the planner pool until its prerequisites are available. The configuration flow may collect the data through a dialog and store it encrypted; it is not correct to promise that every first call can always continue automatically. See the skill importer guide for the admitted cases.

7. Four concrete examples

Four executors actually in use, told from the outside. No source code: just what you ask for and what you get.

7.1 get_now — «what time is it?»

The simplest tool in the catalog. No required arguments. Returns a dictionary with the current date and time.

call: get_now(timezone="Europe/Rome")
response: { ok: true,
 content: "2026-05-06T16:45:23+02:00",
 metadata: { timezone: "Europe/Rome", iso8601: "...", epoch:... } }

No network, no file read, no write. Permissions: time:read. It is one of those tools that look superfluous until you see why they matter: the planner must never invent the date from training memory. When it needs to compute «yesterday's mail», it calls get_now first, then subtracts a day. That way «yesterday» is always today's yesterday, not the yesterday from when the model was trained.

7.2 find_files — «find the photos»

Searches files by name or pattern (the classic «extensions»). Returns the list together with basic metadata: path, name, size, last modified, type.

call: find_files(base_path="/home/user/images", pattern="*.jpg")
response: { ok: true,
 entries: [
 {path: "/home/.../foto1.jpg", size: 2458123,...},
 {path: "/home/.../foto2.jpg", size: 1923456,...},...
 ],
 metadata: { count: 247,... } }

The planner uses it when it has to hand the list off to another tool: for example to keep only the most recent photos, compute total size, compress the older ones. Note that filtering does not live in find_files: the tool returns the files and that is it. If you want a subset, you ask via the pattern, or you pipe the result into a tool that filters. One thing at a time.

7.3 filter_lists — «find overlapping events»

A tool that works on two lists rather than one. Useful when a user question intersects two sets: "which HLT appointments overlap with MNM ones in the next three months?", or "which files are present in both folders?".

call: filter_lists(op="overlap",
 from_step=2, # list A (HLT events)
 with_step=3) # list B (MNM events)
reply: { ok: true,
 op: "overlap",
 entries: [...entries of A that overlap with at least one of B... ],
 metadata: { count_a: 4, count_b: 5, count_out: 0 } }

Available operations: intersect (entries common to both lists, match on a key), union (entries from either, deduped), difference (entries of A not in B), symdiff (symmetric difference), overlap (temporal AND: entries of A whose time window overlaps with at least one in B; auto-detect start/end), and delta (items that are new or have advanced from a reference list, used by incremental monitors).

The taxonomy of list operators is closed and readable: filter_entries reduces a single list (predicates: where_starts_with, where_contains, where_glob, where_regex), filter_lists combines two lists with set ops, compute_entries computes a scalar (sum, average, min, max, count). The three primitives together cover almost every list manipulation without inventing new verbs.

7.4 send_messages — «send a message to a family member»

Sends one or more messages over Telegram or email. Main argument: a list of messages, each with recipient and body.

call: send_messages(messages=[
 {to_user: "lucia", body: "Out for a bit, back at seven."}
 ])
response: { ok: true, ok_count: 1, fail_count: 0,
 results: [{to_user: "lucia", channel: "telegram", message_id: "abc123"}] }

A «transformative» tool: it changes the world, it really sends a message. For this reason, executors that change the world get treated with more care: their manifest declares mail:send and channel:out, and the destination is resolved against the bound user and channel. Delivery is recorded but cannot be undone: the manifest declares revertible=false. Controls must therefore run before delivery. See vaglio for pre-execution checks and approval_ux for how the user is asked to confirm.

8. Going deeper

This document is an introduction. If you want to understand the mechanisms beneath — how a manifest gets signed, how the fence is applied, how the Synt generates code, how the planner picks a tool — the documents below should be read one at a time.

To understand…Read
the planner that picks the right executoragent_runtime
the fence in detail (forbidden paths, exceptions, «sort the photos»)sandbox
how the Synt composes new executorssynt
how an external skill is imported as an executorskill importer
the check that precedes the execution of risky actionsvaglio
how the user sees and approves actionsapproval_ux
the memory that executions leave behindmnest and mnestome
the dialogue with the world (Telegram, web, voice)channel
which models (LLM, embedding, VLM) power the executors and how to change them from a TOMLmodel virtualization
observability (what happened, why, when)observability

You do not need to read these guides in order. Start with the concrete question, follow the relevant links, and stop at the level of detail you need.


Metnos — executor, didactic introduction