✓ Microdesign v1.1 — APPROVED (24 April 2026). Canonical doc approved, aligned with the Architecture v1.1 and the Dialogue on executors and distributed memory. Normative reference for implementation. Replaces the older neuron.html (deprecated, in Italian).
← Documentation index Microdesign › executor

Metnos

executor — anatomy of an executable capability
Microdesign v1.1 — 24 April 2026
Aligned with Architecture v1.1 (ch. 9) and the Dialogue on executors.
Replaces neuron.html (renaming).

Audience: whoever implements the loader, the signing, the sandbox.
Reading time: 18 minutes.

Contents

  1. Scope and boundaries
  2. What an executor is
  3. Anatomy: the six artefacts
  4. Identity: manifest, signature, version
  5. Sandbox profile
  6. Lifecycle: seed, active, fused, quarantined, archived
  7. Synthesis (synt): from proto-mnest to executor
  8. Execution: invocation from the gateway
  9. Audit and observability
  10. Examples: three seeds and one builtin
  11. Renaming: neuron → executor
  12. Remote executors: same contract, process elsewhere
  13. Open questions

1. Scope and boundaries

This document defines what an executor is, how it is built, how it is authenticated, how it is isolated, how it is born, how it dies. It is the first of three canonical docs introduced by the Dialogue on executors and distributed memory (24 April 2026), together with mnest and mnestome. It replaces the old neuron.html: technically the same object, but the name «neuron» carried a biological metaphor that became cumbersome at the implementation level. Executor is more precise: it is executable code.

Boundaries

This document covers:

It does not cover, and defers to other docs:

2. What an executor is

An executor is a unit of code that Metnos can run as a step of its own reasoning. A function with a public contract, a signed identity, an isolation profile, and a manifest that describes what it does and which resources it touches. Nothing more. Not an agent, not a microservice, not a plugin: a small executable block under the project's signature.

The operational definition. An executor is what the gateway can invoke. Everything else (identity, sandbox, audit) exists to make that invocation safe and traceable. When a design doubt arises, the question of clarity is always the same: «is this something to be invoked, or something to be decided?» If decided, it is not an executor: it is policy.

Three categories, one external anatomy

CategoryDescriptionLives inSignature
Seed The ones Metnos finds at first boot: about twenty, hand-written in the repository, indispensable to basic operation. Examples: fs_read, fs_write, shell_exec, web_fetch, telegram_send. workspace/executors/<n>/ Ed25519 per-instance
Synthesised Born over time from the synt (ch. 7) to fill a gap detected by use. They share the same anatomy as the seeds and are born through the same signing and approval pipeline. workspace/executors/<n>/ Ed25519 per-instance
Builtin Part of the runtime, not workspace artefacts. They implement services that need the system clock, the gateway's async loop, or transversal persistent state (e.g. scheduler). They may carry capabilities that no user-level executor would ever obtain. Metnos source code release-signed

The distinction between seed and synthesised is historical, not structural: a seed is generated manually before first boot, a synthesised one is generated automatically while the system is alive. Once signed, the gateway treats them in exactly the same way.

Builtins are different: they share the external contract with seeds and synthesised (same run(args, ctx) -> dict, same audit, same I/O schema), but they do not have a separate manifest.toml, no per-instance profile.lock, no synthesis pipeline. They live in the Metnos sources and are signed as part of the release. The synt can never generate a builtin (ch. 7): they are foundational, not synthesisable.

3. Anatomy: the six artefacts

An executor lives under workspace/executors/<name>/. The directory contains six artefacts, in this reading order:

ArtefactFileDescription
Manifest manifest.toml Identity, version, author, description, declared sandbox profile, error contract. See ch. 4.
Signature manifest.sig Ed25519 signature over the manifest and the executable hash. Without a valid signature, the gateway refuses to invoke.
Code main.py The implementation. A single entry function exposed as def run(args: dict, ctx: ExecutorCtx) -> dict. No dynamic import, no eval, no exec.
I/O schema schema.json JSON Schema for input parameters and result. Validated both on entry (gateway) and on exit (executor host process).
Birth tests tests/birth.py A handful of cases that demonstrate the expected behaviour. The synthesiser (ch. 7) imposes them as a barrier for approving a new executor; the loader re-runs them at every boot to guarantee continuity.
Profile lock profile.lock A frozen hash of the effective sandbox profile (ch. 5) computed at signing time. If at runtime the concrete profile does not match the hash, the invocation fails: a guard against silent modifications.

4. Identity: manifest, signature, version

The manifest

The manifest is the identity document of an executor. Example for fs_read:

[executor]
name        = "fs_read"
version     = "1.0.0"
created_at  = 2026-04-25T08:12:00Z
created_by  = "seed"               # or: "synt:<run_id>"
summary     = "Read a file from the workspace and return its content."

[contract]
input_schema  = "schema.json#/definitions/Input"
output_schema = "schema.json#/definitions/Output"
error_classes = ["NotFound", "PermissionDenied", "TooLarge"]
idempotent    = true
side_effects  = false

[sandbox]
profile = "fs-read-workspace"      # see sandbox.html
hash    = "blake3:abc123..."       # fingerprint of the profile

[audit]
trace_topic = "executor.fs_read"

Three fields are normative:

The signature

The file manifest.sig contains the Ed25519 signature (curve 25519, private key under ~/.config/metnos/keys/ with 600 permissions, never versioned) over these concatenated bytes:

blake3(manifest.toml) || blake3(main.py) || blake3(schema.json) || profile.lock

The gateway loader, at boot and at every request, verifies:

  1. the signature is valid against the project's public key;
  2. the four hashes match the files on disk;
  3. the manifest is semantically coherent (valid schema, existing profile).

If even one check fails, the executor is rejected. Not loaded, not invoked, not deleted: it is moved to quarantine (ch. 6) and surfaced to the user.

Versioning

A version of an executor is never modified «in place». To change something, a new version is created (e.g. 1.0.02.0.0) with its own signature. The gateway can keep multiple versions loaded in parallel as long as there are mnests citing them (see mnest.html). Promoting a new version to default is an explicit, signed act: it is recorded in workspace/executors/<name>/CURRENT.

5. Sandbox profile

The profile is the point where an executor's identity meets perimeter safety (Architecture ch. 5). The section is longer than usual because two distinctions must be pinned down, and the question «can the executor read outside the sandbox?» deserves an operational answer, not a slogan.

5.1 Declared profile vs applied sandbox

Two different things, coupled, often confused:

LayerWhat it isLives in
Declared profile A declaration, written in the executor's manifest, of which resources the executor needs: which paths to read, which to write, which domains to reach, which binaries (if any) to execute, with which time and memory caps. manifest.toml (static, signed).
Applied sandbox The technical enforcement of those limits on the process running the executor's code. On metnos-server we use bubblewrap + landlock + mount namespace + seccomp + a dedicated network interface. That process, physically, cannot see or call anything the profile has not granted. Gateway runtime configuration, generated from the profile.

The two layers are kept in sync by the profile lock described in ch. 3: the hash of the concrete profile, computed at signing time, is frozen in profile.lock. At every invocation, the gateway loader recomputes the hash of the sandbox it is about to apply and compares it with the lock; on mismatch, the invocation fails. A silent profile change — by anyone — is intercepted.

Why two layers and not one. One could think the sandbox alone is enough: it is the one that actually enforces. The declared profile, however, serves three distinct purposes. (a) It is readable by the vaglio before the invocation, so a call that obviously asks for too much (an fs_read on /etc/passwd) is rejected before any sandbox is opened. (b) It lets the synt generate minimal profiles at synthesis time (ch. 7). (c) It is living documentation: anyone reading the manifest sees at once what the executor demands, without having to read the runtime configuration.

5.2 The five dimensions of the profile

DimensionExample policies
Filesystem readnone; workspace/inbox/; workspace/<sub> read-only; HOME read-only.
Filesystem writenone; workspace/<sub>; private temp directory.
Shell executionforbidden; allowed only for listed binaries; free (discouraged).
Outbound networknone; allow-list of domains; free (discouraged).
Resourcesmax memory, max CPU, max duration.

5.3 Can it read outside the sandbox? No, never. Why.

Short answer: no. What the profile does not grant, the executor's process does not see and cannot see. This is not a style recommendation, it is a kernel constraint.

Long answer, in three steps:

  1. Mount namespace. bubblewrap opens the process in a private mount namespace where only the paths listed in the profile are visible (mounted read-only or read-write according to the field). The rest of the filesystem simply does not exist from the process's point of view. An open('/etc/passwd') returns ENOENT (file not found), as if the file had never been there.
  2. Landlock. On top of the mount namespace, the kernel applies a landlock policy that further restricts: even paths mounted read-only cannot be opened for writing, even writable paths cannot be opened with dangerous flags. A violation returns EACCES.
  3. Seccomp. Network syscalls are filtered by seccomp: an executor with no network in its profile receives EPERM on connect(). An executor with allow-listed network goes through a local proxy that blocks anything outside the allow-list.

This is defence in depth: the first level (mount namespace) already suffices for unlisted paths; the second (landlock) handles read/write nuances; the third (seccomp) closes the network side. Three independent kernel mechanisms, configured by the profile. Bypassing them would require a kernel bug, not an executor bug.

5.4 Operational example: fs_read called wrong

Suppose the gateway receives the request: fs_read({path: "/etc/passwd"}). The executor fs_read has profile fs-read-workspace which grants read-only on workspace/. What happens?

#ComponentWhat it does
1Policy validation in the gatewayBefore opening any sandbox, the gateway compares the path argument with the declared profile. /etc/passwd is not under workspace/: the call is rejected with a structured PolicyViolation error. No sandbox is even opened.
2(Defence in depth)If, by absurd hypothesis, the gateway had been confused and had opened the sandbox, the executor's process would have been in a mount namespace without /etc/. open('/etc/passwd') would have returned ENOENT. The executor would have returned NotFound.
3(Further defence in depth)If the path /etc/passwd had been mistakenly mounted, landlock would have rejected the read() with EACCES.
4AuditIn all cases, the gateway writes to the audit log the request, the outcome (PolicyViolation, NotFound, PermissionDenied), the sender, the timestamp.

The correct design is that the error is intercepted at step 1 (policy-time), not at step 2 or 3 (kernel-time): it is faster, more informative for the caller, and leaves no traces in the sandbox. Steps 2 and 3 are the safety net that exists if the first should fail because of a bug of ours.

5.5 Core forbidden paths: above the profile

Some paths no profile can grant, even if the manifest declared them. They are the core forbidden paths, hard-coded in the gateway's code, irreducible, modifiable only by rite (code change, review, release):

Conceptual difference: the profile is per-executor declared modulation; the core forbidden paths are universal system prohibitions. They live on two different planes of axis 2 of perimeter safety (Architecture ch. 5): the modulable shell (profiles) and the hard core (forbidden paths). The Full autonomy level can widen the shell, never the core.

The principle of least privilege is the only one allowed. An executor that asks for «free network» when it only needs api.openai.com is not approved. The synt, during synthesis (ch. 7), tries to generate the tightest profile that still satisfies the birth tests; if it cannot, it suspends synthesis and asks Roberto to narrow it manually.

5.6 Can I derogate? Paths outside the workspace, and the «tidy the photos» case

Yes. Nothing in the design forbids a profile from declaring paths outside workspace/. The profile is an explicit declaration: one can declare ~/Pictures/, ~/Documents/work/, a network-mounted folder, a backup partition. What the profile declares, the sandbox grants; what the core forbids stays forbidden anyway (ch. 5.5).

The derogation, however, is a weighed act. It has three constraints:

  1. It is explicit. It must be declared in the manifest, not implicit. A reviewer reading the manifest must see immediately that this executor touches ~/Pictures/ and not just the workspace.
  2. It is signed. A widened profile changes the profile.lock hash; changing the profile means changing the version, and changing the version requires a new signature and a new approval. The sandbox cannot be widened in secret.
  3. It stays under the core. Even a profile that declares ~/ read+write cannot access ~/.ssh/, ~/.gnupg/, ~/.config/myclaw/: the core forbidden paths (ch. 5.5) are masked by the mount namespace even inside a «wide» profile.

Use case: tidy the photos

A real case. The photos live in ~/Pictures/ (on the server) or in C:\Users\Roberto\Pictures\ (on the laptop). The two situations are distinct and are solved distinctly.

CaseHow
Tidy on metnos-server (photos in the server's ~/Pictures/) Create an executor photo_organize with dedicated profile: fs-read-write on ~/Pictures/; no shell; no network; max duration 30s; bounded output. The profile is explicit, signed, subject to synt synthesis and to Roberto's explicit approval. The core continues to forbid ~/.ssh etc., so even a bug of the executor cannot leave ~/Pictures/.
Tidy on the laptop (photos in C:\Users\Roberto\Pictures\, off metnos-server) The remote executor case: the process performing the action runs on the laptop, not on the server, because syncing gigabytes of photos just to tidy them does not make sense (see Architecture ch. 4 for the full motivation). Sandboxing logic changes: see ch. 12 of this doc.
The practical rule. The workspace is the safe default: anything that can be done in the workspace, is done in the workspace. One leaves the workspace only when the operation loses meaning by staying inside — typically because the data physically lives elsewhere (photos, music archive, work code synced by an IDE). In those cases the derogation is declared explicitly, and the derogation lives in the manifest so anyone re-reading the system knows exactly what that executor can touch.

6. Lifecycle: seed, active, fused, quarantined, archived

Seed ~20 at boot Active in use Fused A+B → AB Quarantined broken sig or failed test Below threshold low usage, ager active Archived inactive boot fusion drift disuse approve archive with reason
Figure 1 — State machine of an executor. Blue arrows are ordinary life transitions; red ones require an explicit reason. The model formalises the four states discussed in the Dialogue on executors, Fourth Giornata.
StateMeaningOutgoing transition
SeedExecutor present at first boot, hand-written in the repo.→ Active (after signature check).
ActiveLoaded by the gateway, invocable, with at least one mnest citing it.→ Fused, Quarantined, Below threshold.
FusedTwo executors with overlapping traces have been merged by the synt into a new executor that covers both. The old ones stay loaded as long as residual mnests cite them, then archived.→ Archived.
QuarantinedInvalid signature, failed birth test at boot, profile lock mismatch. Not invocable, not deleted. Surfaced to Roberto with a warning.→ Active (after re-approval) or → Archived.
Below thresholdUsage in the last N days below the configured threshold. The ager proposes archival. Stays loaded until Roberto confirms.→ Active (use resumed) or → Archived.
ArchivedMoved to workspace/executors/.archive/, not loaded by the gateway, but kept for historical trace and possible rehabilitation.→ Active (explicit rehabilitation).
Reversibility. All transitions are reversible (ch. 14 of the Architecture): archiving an executor does not delete it, it moves it to .archive/; defusing a fusion reloads the two original executors; a quarantined one returns to active after a new valid signature. The only thing that is irreversible is the trace in the audit log.
Reduced lifecycle for builtins. Builtins do not pass through the state machine above. They have only two operational states: active (running as part of the runtime) and disabled in config (recognised by the gateway but not started). No quarantine (they cannot drift: the signature is the Metnos release's), no archiving (removal requires a new release), no fusion (they are not synthesisable). This reduction is the price of their position: they are foundational, and the pool's evolutionary mechanisms do not touch them.

7. Synthesis (synt): from proto-mnest to executor

The synt is the process that brings new executors to life when use demands it. The trigger source is a recurring proto-mnest: a trace of unsatisfied desire («the output of A was meant to go to B which does not yet exist») that recurs several times in the same month.

The pipeline has seven stages. Each stage may suspend and ask the user.

#StageWhat it does
1Pattern detectThe ager counts recurring proto-mnests; if one crosses the recurrence threshold, it offers the synt a motivation card.
2SpecificationThe synt drafts a textual specification (what the executor must do, which inputs, which outputs, which errors). Roberto approves or amends.
3SkeletonGenerates main.py and schema.json by calling the LLM with the specification as context.
4ProfileComputes the tightest sandbox profile compatible with the birth tests. If it cannot, it suspends and asks.
5Birth testsGenerates or accepts from the user the cases in tests/birth.py. Runs them: all must pass with the profile from step 4.
6Human approvalRoberto sees manifest, code, tests, profile. Approves, amends, or rejects. On approval, signing starts.
7Sign and installComputes the hashes, generates the Ed25519 signature, writes profile.lock, moves the executor into workspace/executors/<name>/ and marks it as Active.

The split is sharp: synt proposes, the user approves. No synthesis without a human filter; this is the third of the six principles (ch. 14 of the Architecture). The detail of the pipeline lives in synthesizer.html (in Italian and pending rewrite).

7.1 The cascade: compose first, generate later

The seven-stage pipeline above is the generation arm: it produces a brand-new executor. But it is not the first tool the synt reaches for when a proto-mnest recurs. The canonical sequence is a cascade:

WhenStrategyWhat it doesFrontier-LLM cost
Reactive (user turn) Compose Scans the active pool and tries to build a chain of existing executors that closes the proto-mnest. If found, it proposes the chain as orchestration — no new executor, no signature to issue. It leaves a proto-mnest pointing at the composition: if it recurs, that becomes a candidate for promotion (an introspective step). zero
Reactive (fallback) Generate Seven-stage pipeline (above). It fires only if no composition exists or if the chain is too long to remain readable. ~1 Spec call + 1–2 Draft
Introspective (nightly homeostasis) Merge Two executors with overlapping traces and compatible profiles get fused into one. See lifecycle fused (ch. 6). 1 Spec call for the fused
Introspective Generalise N specialised executors with the same shape get proposed as a single broader, parametric executor. Trigger: threshold of recurring proto-mnests on adjacent dimensions. 1–2 calls
Introspective (rare) Specialise From a general executor, derive a specialised version for a hot case. Only with measurable benefit (not preemptive optimisation). 1 call
Why the cascade. The guiding principle is operational, not aesthetic: an executor library grows well when each new piece has a reason to exist that is not already covered by a composition of older pieces. Composing first pays a double dividend — it does not spend frontier calls needlessly and it keeps the pool small. Generation remains lawful, but becomes the documented exception.
The non-retreat telos. The cascade is the mechanism through which the synt honours the «cultivate the tools» telos (Architecture ch. 11): as long as the user request is within the constitution and within the budget, the synt exhausts the available strategies before answering «I cannot». Failure remains lawful but traced — never a silent retreat. The abandonment threshold (max 3 retries per stage, suspension on pattern-rejected requests) still holds: non-retreat is courage, not stubbornness.

The three introspective steps (merge, generalise, specialise) do not yet have a dedicated doc; the natural home is either an extra chapter within the future rewritten synt, or a new consolidator.html. Open decision.

8. Execution: invocation from the gateway

The flow of an invocation from the gateway to an executor goes through eight steps. Here is the high-level sequence; the detail of intermediate steps (Policy, Vaglio, sandbox) lives in their respective docs, all pending rewrite.

  1. The gateway receives an action: call executor X with input {...}.
  2. It resolves X via workspace/executors/X/CURRENT; if there is no CURRENT or it is quarantined, it fails.
  3. It verifies signature and profile lock. If broken, quarantine and failure.
  4. It validates the input against schema.json. If invalid, structured-error failure.
  5. It opens the sandbox with the declared profile. The four Laws act here as an additional filter.
  6. It runs run(args, ctx). Time limit, memory limit, network limit.
  7. It validates the output against schema.json. If invalid, failure.
  8. It writes to the audit log: who called, with what, for how long, what came back (in normalised form, with secrets redacted).

Along the flow, the gateway updates two structures:

9. Audit and observability

Every invocation produces a JSONL line in workspace/.audit/executors/YYYY-MM-DD.jsonl:

{
  "ts":         "2026-04-25T08:14:33.881Z",
  "trace_id":   "01HW...",
  "turn_id":    "01HX...",
  "executor":   "fs_read",
  "version":   "1.0.0",
  "caller":     {"kind": "user", "sender": "roberto", "channel": "telegram"},
  "input":      {"path": "workspace/notes/journal.md"},
  "output":     {"size": 4231, "sha":"blake3:..."},
  "duration_ms": 18,
  "exit":       "ok"
}

Three invariants the loader and the runtime guarantee:

  1. append-only: the audit file is never modified; corrections are written as a subsequent rectification line.
  2. self-describing: every line carries everything needed to reconstruct the invocation, without cross-referencing other sources.
  3. redacted: recognised secrets (API keys, passwords, tokens) are replaced by a placeholder with their hash, never stored as plaintext.

10. Examples: three seeds and one builtin

Three seed examples

fs_read

Reads a file from the workspace. Profile: read-only filesystem on workspace/; no shell, no network; max duration 2 seconds; max output 4 MB. Errors: NotFound, PermissionDenied, TooLarge. Idempotent.

web_fetch

Fetches a web page and returns it as text or markdown. Profile: no filesystem, outbound network on HTTP/HTTPS toward an allow-list of domains (configurable per instance); max duration 15 seconds; max output 2 MB. Errors: DnsError, Timeout, Forbidden, TooLarge. Idempotent.

telegram_send

Sends a message to the user's trusted chat. Profile: no filesystem, outbound network only to api.telegram.org; max duration 5 seconds. Side effects: yes (sends a message). Errors: RateLimited, Unauthorized, Network. Not idempotent: two calls with the same input send two messages.

Side effects and idempotency. An executor with side_effects = true and idempotent = false is subject to reinforced Vaglio: the gateway asks for confirmation even within the Full autonomy level. Precautionary asymmetry (ch. 5 of the Architecture) is the foundation.

A builtin example: scheduler

scheduler is the first (and so far only) builtin executor proposed: it invokes another executor (or chain) at a defined cadence. It is the mechanism that makes «requests with schedule» possible without inventing a separate design category. It replaces the wrong idea of «agreed routines» as a stand-alone object.

Contract

FieldTypeMeaning
target_executorstr (or chain)The executor to invoke at every firing.
argsdictArguments passed to the target.
schedulestrCron-like ("0 8 * * *") or NL ("every morning at 8") translated to cron at creation time.
delivery_channelstrChannel for the result (Telegram, mail, voice, …).
countint | NoneNone = unlimited firings; 1 = one-shot; N ≥ 1 = exactly N firings.
expirytimestamp | NoneTime-based expiration. The first of count and expiry closes the schedule.
on_failureenumskip / retry / notify / cancel. Default notify after 1 retry.
max_consecutive_failuresint | NoneAuto-cancel after N consecutive failures. Default 3, None to disable.
pausedboolManual pause without cancelling. Default false.

Output: {schedule_id: ULID, next_fire: timestamp}.

Sibling operations, also builtin: scheduler.list (list active schedules), scheduler.cancel (close a schedule), scheduler.modify (change cadence, expiry, count or channel). Every modification passes through the human gate if it changes cadence or target_executor.

Profile (declared in code, not in a manifest)

Audit

Every firing emits a JSONL line like any other executor invocation, with caller = {"kind": "scheduler", "schedule_id": "…"}. A schedule's history is reconstructable by schedule_id: creation, modifications, every firing, eventual closure.

Reduced lifecycle in practice. scheduler, like all builtins, does not pass through the state machine of ch. 6: it has only active and disabled in config. Its schedules, however (the rows in the schedule store), have their own small lifecycle: active, paused, completed (count or expiry reached), cancelled (by user or by on_failure=cancel). Lifecycle internal to the builtin, not reflected in the mnestome graph.

scheduler is the first builtin because it solves a recurring case (mailbox monitoring, daily summary, periodic checks) with a mechanism that composes naturally with the synt cascade: a recurring schedule becomes a candidate for fusion (synt ch. 5.1) into a single executor that incorporates the chain.

11. Renaming: neuron → executor

The previous microdesign used the term neuron (neuron.html, now deprecated and marked untrusted). The Dialogue on executors and distributed memory renamed it to executor for two reasons:

  1. Precision. A biological neuron is not signed executable code, has no manifest, has no sandbox profile. The metaphor helped early brainstorming but became a burden in technical docs.
  2. Alignment with execution. The gateway speaks to executors: executable code invoked to produce a result. The name remembers that verb.

The biological term still works well to describe the emergent mnestome as a graph: that is a different layer of metaphor (graph as tissue), to which we return in the dedicated docs.

12. Remote executors: same contract, process elsewhere

The topology (Architecture ch. 4) states that some executors, in the future, will not run on metnos-server but on the user's machines: the laptop first of all. This chapter says what changes, in concrete terms, for the executor microdesign when the process no longer lives on the server.

What does NOT change

The contract of a remote executor is identical to that of a local one. Same anatomy (ch. 3): TOML manifest, Ed25519 signature, main.py code, I/O schema, birth tests, profile lock. Same synthesis pipeline (ch. 7). Same gateway-side identity (ch. 4). Same lifecycle (ch. 6). Same central audit log, on metnos-server (ch. 9): who calls a remote executor and what comes back is traced exactly as for a local one.

The remote executor's manifest still lives on the server metnos-server, under workspace/executors/<name>/. The server is the canonical source of the «who» and the «what». On the laptop only the execution lives: a small confined process, launched and supervised from the server side through the Headscale channel (see Architecture ch. 4).

What changes, and why

DimensionOn metnos-serverOn the laptop (remote executor)
Technical sandbox Strong: bubblewrap + landlock + seccomp + mount namespace. Three independent Linux kernel mechanisms. Weaker: Windows has no direct equivalents. AppContainer / Job Object / NTFS permissions help but do not give the same boundaries. The sandbox is more a confined user-space process than a true kernel jail.
Core forbidden paths Enforced by the system: ~/.ssh, ~/.gnupg, etc. Enforced twice: once declared in the profile (server-side, part of the signature), once verified again by the small runtime on the laptop before every operation. We do not trust a single layer when the technical sandbox is weak.
Pairing Implicit: the process runs on the same machine as the gateway, of which it is a child. Explicit: the device (laptop) must be paired through a ceremony signed by the user from an already-trusted channel. A new laptop cannot run remote executors until it has been admitted.
Channel authentication Not needed: all in-process. mTLS inside the Headscale overlay, or WireGuard preshared key + per-call signed token. The gateway rejects unauthenticated calls.
Idempotency Recommended, not strictly required. Mandatory. The network between metnos-server and the laptop can drop mid-call. Every invocation carries a unique identifier; two invocations with the same id produce the same outcome (the second is a no-op).
Reversibility Generic model (ch. 6). «Before/after» model: before performing an operation that changes laptop state, the gateway writes on metnos-server a manifest of the «before» (list of touched paths, hashes, sizes). If Roberto says «undo», the same pattern is applied in reverse on the laptop. See Architecture ch. 4 sec. «The before/after pattern».
Audit One JSONL line on metnos-server. Two corresponding lines: one on the server (who called, when), one on the laptop (what was done on the filesystem, when). The two correlate by trace_id.
Version drift One single directory workspace/executors/<name>/. Two copies of the binary: one on the server (canonical), one on the laptop (cache). At every invocation, the gateway verifies that the binary hash on the laptop matches the signed manifest; if not, it suspends and requests update.

Example: photo_organize on the laptop

The canonical example from ch. 5.6: tidy a photo archive of ten thousand files that live in C:\Users\Roberto\Pictures\. Syncing them to the server, tidying them, syncing back implies gigabytes of traffic, double disk usage, time windows in which the same file exists in two states on two machines. An in-place rename performed directly by the laptop solves it in one second. But the process that performs that rename must be trusted and traced like every other Metnos executor.

The flow, in concrete terms:

  1. Synthesis. The synt (ch. 7) proposes photo_organize with manifest declaring: input = list of sort rules, output = number of tidied files, profile = read-write on C:\Users\Roberto\Pictures\ (excluding reserved sub-folders), no shell, no network. Roberto approves, the manifest is signed on the server.
  2. Device pairing. From Telegram, Roberto confirms that the laptop «Acme-15» may run remote executors. The ceremony generates a pairing key that lives in the mnestome and in the small runtime on the laptop.
  3. Distribution. The gateway sends the signed binary of photo_organize to the laptop, which verifies it against the manifest and writes it to local cache.
  4. Invocation. Roberto says «tidy the 2024 photos by capture date». The gateway prepares a call with unique trace_id, writes on metnos-server the «before» manifest (list of paths that will be renamed, hashes), then sends the call to the laptop via Headscale + mTLS.
  5. Local execution. The small runtime on the laptop verifies authenticity and signature, opens the weak sandbox (AppContainer/NTFS permissions) with the declared profile, performs the rename, returns the outcome.
  6. Audit. Two JSONL lines: one on metnos-server (who called, when, outcome), one on the laptop (list of paths actually touched, hashes after). Correlated by trace_id.
  7. Possible undo. If Roberto changes his mind, the gateway reads the «before» manifest and sends the laptop the inverse call. Reversibility is preserved.
Not on day one. All this is future direction: remote executors are roadmap phase 5+ (Architecture ch. 17), and they are introduced one at a time, motivated by a concrete use case. The five security questions of Architecture ch. 4 (closing callout) all need to be answered before the first real remote executor. This chapter says how it should be done, not what is already ready.

13. Open questions

  1. Signing key. The Ed25519 key lives under ~/.config/metnos/keys/. When a new device must be allowed to sign new executors (e.g. Roberto's laptop on the road), how is it authorised? Device pairing (ch. 4 of the Architecture) plus time-bound delegation? Open.
  2. Sharing across instances. If Metnos becomes multi-instance in the future (remote executors on a second device), how does the signature of a new executor propagate? Replication with verification on the remote side? Open.
  3. Versions in flight. A new version of an executor under test coexists with the old one. For how long? Is promotion manual or does it have an automatic threshold? Open, see roadmap phase 4.
  4. Manifest validation. Will we use a TOML parser that exposes positional errors? Is there a compatibility test with the toolchain? Open.

next in the triad
mnest
The trace of co-activation between two executors.
third in the triad
mnestome
The emergent graph of all mnests.
level 1
Architecture, ch. 9
The big picture: executor, mnest, mnestome.
index
Microdesign
All docs.

Metnos — executor microdesign v1.1 — 2026-04-24
New canonical doc. Replaces neuron.html (deprecated).