Modules
This module is the map — what exists and why. Each entry defends a module's
existence; how a module is designed lives in its own @moduledoc.
The MCP mount — the node's entire external surface.
The HTTP entry point: routes paths to YmerNode.Mcp, and answers 404 to
everything else.
The instructions the node hands every MCP client at connect.
The four helpers a tool's action and format layers share, and nothing else.
Wymcp tool notebook — an open SQL surface over notebook.db, the node's own
private store, together with the actions that keep it safe.
Action implementations for the notebook MCP tool
(YmerNode.Mcp.Tools.Notebook, which calls run/2) — a thin boundary over
YmerNode.Notebook and YmerNode.Notebook.Backup. This is a trust boundary:
sql, table and id are guarded is_binary in each head because nothing
downstream validates them — the MCP dispatch layer checks that a parameter is
present but never what type it holds — so sql and table flow straight into
SQLite and id flows into filename construction. Each action returns
{:ok, data, hint_ctx} or {:error, {reason, ctx}} — and, where the error is one
the caller can act on, {:error, {reason, ctx}, hint_ctx}, the three-element form
that puts a follow-up hint on the error payload rather than only in its prose.
Translates YmerNode.Notebook and YmerNode.Notebook.Backup error reasons into
LLM-readable messages. SQLite errors are surfaced verbatim so the agent can read
and fix its own SQL; backup failures never are, because their detail carries
filesystem paths the caller must not see.
Shapes YmerNode.Notebook and YmerNode.Notebook.Backup results into
JSON-serialisable maps for MCP responses.
Query results pass through as parallel columns/rows arrays (compact and
order-preserving) rather than per-row maps.
Follow-up hints for the notebook tool, keyed off the hint_context maps
YmerNode.Mcp.Tools.Notebook.Actions produces. After execute creates schema, the
natural next steps are describing or querying it; after a capture the natural
next step is confirming it. A completed restore needs no follow-up either, since
the roll-back is already done — but a restore that failed recoverably does: the
hint carries the id of the backup to restore, or of the restore to retry, so the
recovery is one call away instead of a re-read of the error text. Everything else
falls through to none — including every answer about a notebook that cannot take
the call, whose next action is a person's and not another tool call, and a
backup that will not open, whose next action is a different id this module has
no way to choose.
Schema definitions for the notebook tool's actions. The :notes/:examples are the
agent's primary documentation for sqlite-vec usage and the INTEGER-PRIMARY-KEY
join discipline — they surface via the help tool.
Wymcp tool references — the registry of pointers at where knowledge lives,
and the cache of what their targets say.
Action implementations for the references tool — the boundary over the
YmerNode.References context.
Translates the references tool's error reasons, optionally with context,
into messages a worker can act on. Called by
YmerNode.Mcp.Tools.References's Wymcp.Tool.handle_error/1.
Response shaping for the references tool.
Follow-up hints for the references tool, keyed off the hint_context maps
YmerNode.Mcp.Tools.References.Actions produces.
Action schemas for the references tool. The notes surface through the
help tool and are the worker-facing contract documentation — the mode
contract, the duplicate rule, the read window, and the membrane
(YmerNode.References § The membrane).
Value validation for the references tool's params.
Wymcp tool schedules — adding, listing, updating and removing the standing
instructions YmerNode.Schedules fires, and listing and stopping the watches
the references tool starts.
Action implementations for the schedules tool — the boundary over
YmerNode.Schedules.
Translates the schedules tool's error reasons into messages a worker can act
on. Called by YmerNode.Mcp.Tools.Schedules's Wymcp.Tool.handle_error/1.
Action schemas for the schedules tool. The notes surface through the help
tool and are the worker-facing contract: what a cron expression and a lifetime
look like, what add refuses rather than leaving to a firing, and what a
firing skips.
Wymcp tool script_author — writing the scripts this node accepts.
Action implementations for the script_author tool — the boundary over
YmerNode.Scripts's write verbs.
Translates the script_author tool's error reasons into messages a worker can
act on. Called by YmerNode.Mcp.Tools.ScriptAuthor's
Wymcp.Tool.handle_error/1.
Follow-up hints for the script_author tool, keyed off the hint_context maps
YmerNode.Mcp.Tools.ScriptAuthor.Actions produces.
Action schemas for the script_author tool. The notes surface through the
help tool and are the worker-facing contract of each call — what it does,
what it refuses, what its flags mean. The script contract itself is not
written here: scripts guide renders it from YmerNode.Script's own docs,
and the notes of check and create point there.
Response shaping for both script tools — YmerNode.Mcp.Tools.Scripts and
YmerNode.Mcp.Tools.ScriptAuthor.
Wymcp tool scripts — the scripts this node has accepted, and running one.
Action implementations for the scripts tool — the boundary over
YmerNode.Scripts.
Translates the scripts tool's error reasons into messages a worker can act
on. Called by YmerNode.Mcp.Tools.Scripts's Wymcp.Tool.handle_error/1.
Follow-up hints for the scripts tool, keyed off the hint_context maps
YmerNode.Mcp.Tools.Scripts.Actions produces.
Action schemas for the scripts tool. The notes surface through the help
tool and are the worker-facing contract: what a run costs, what acceptance
means, and why describe is the call to make before run.
The notebook — notebook.db, a separate, portable SQLite store of user- and
LLM-governed memories, working and long-term: agent-defined schema, open SQL,
embeddings, backup and restore. Potentially precious (tutoring records,
curated knowledge); the node's obligation is durability — governance stays with
the user and the LLM. This module is its raw-SQL surface (via
YmerNode.Notebook.Repo).
Capture and restore for notebook.db — the node's one durable obligation.
Singleton mutex serializing YmerNode.Notebook.Backup operations — capture and
restore.
Ecto repo for notebook.db — the node's one deliberate local store.
One-shot boot probe that aborts application start when the sqlite-vec vec0
extension did not load into YmerNode.Notebook.Repo.
The registry of pointers at where knowledge lives, served to LLM workers as
the references tool, and beside it the cache of what their targets say —
§ The membrane is the rule between the two.
The cache: what the targets of this node's references say, fetched by each
reference's own script and kept here — one cache entry per reference, text
in the node database, files in the cache directory. What the cache is for,
and the rule that keeps a reference a pointer while its content lives here,
is YmerNode.References § The membrane.
The boot step that makes the cache directory rebuildable with the node
database: it runs YmerNode.References.Cache.reconcile/0 once, after the
migrator and before anything can fill an entry, and leaves no process
behind.
One window onto a text cache entry: the lines from offset, at most limit
of them, and never more bytes than the budget — whichever comes first.
One reference's cache entry: what the script its fetch recipe names last
answered for it, and when. How an entry is filled, refreshed and served is
YmerNode.References.Cache's; this is the row.
One row of the registry: a pointer naming where knowledge lives and when to
look, never what it says — that is its cache entry's
(YmerNode.References.CacheEntry). The rule between the two — the membrane —
is stated in YmerNode.References.
The find surface — two composable layers on one call.
Derives a reference's source and fetch recipe from its uri, at read time and never from storage.
Ecto repo for node.db — the node database, and the rebuildable half of what
the node stores.
The node's schedules: standing instructions to run one action of one accepted
script, with fixed args, on a cron expression in the node's time zone, until
each one's lifetime ends — and the watches that
keep references' cache entries current (§ Watches). They are added, listed,
updated and removed through the schedules tool and the CLI, a watch is
started and stopped through the references tool, and all of them are fired
by YmerNode.Schedules.Scheduler.
A schedule's cron expression: which ones the node accepts, and when one comes
due next in the node's time zone. The crontab package parses and walks them;
this module decides only what that package leaves open.
Which schedules have a run in flight right now, and until when at the latest — a process registry of its own, unique by schedule, whose entry a firing takes for as long as its run lasts.
What a schedule's row keeps of its last run: the moment the firing that started
it came due, one of four outcomes, the runner's own message, and how long the
run took. Nothing before it — there is no run history, and a skipped firing
leaves no trace — because the node is a script runner, not a reliable
scheduler. What a script produces goes where the script puts it; anything it
wants kept beyond one line, it writes to the notebook. A watch's firing is the
one whose answer is kept, and the cache keeps it
(YmerNode.References.Cache), not this row.
A schedule's lifetime — how far into the future it keeps firing — read from what
a caller gives add or update into the one instant it ends.
A standing instruction for the node to run one action of one accepted script,
with fixed args, on a cron expression in the node time zone, until its lifetime
ends — or a watch, a reference's own schedule, which names no script and no
action because each firing derives the reference's recipe afresh
(YmerNode.Schedules § Watches).
The process that fires schedules: once a minute, on the minute, it reads the
active schedules and starts a firing for each one due in that minute — each in
a task of its own under YmerNode.Schedules.FiringSupervisor, so a long run
never holds the next minute back.
The contract a script implements — its description, its actions and their
read/write marks, its declarations — and the version use YmerNode.Script
stamps into its row.
What the node hands every run — Req, JSON, the notebook, secrets, the
throttles it declares, the node's time zone, the files directory, the Typst
renders and browser calls — provided by the node rather than declared by the script.
What a VM running script code outside the node needs, for a test or a live run: the processes a script's run reaches for, and the three configuration keys the node's own environments would otherwise set.
Testing a script in a repository of its own: an ExUnit setup callback and
the functions a script test calls, over YmerNode.Script.Harness. Nothing is
used — as with Req.Test, a test module names what it wants in setup.
The scripts this node has accepted, and every door they arrive and leave through — the context both script tools wrap and the node's own callers use.
The node's half of the browser service: the Node program in
browser-service/ that a user starts on their own machine, outside the node,
which runs Playwright code a script sends it — each browser call in a fresh
browser context — and keeps the storage states.
A browser call's failure — the exception YmerNode.Script.Context.playwright/3
answers as {:error, exception}, the way YmerNode.Scripts.Throttle.Error
is what YmerNode.Script.Context.request/2 answers for a refused request: a
script matches on kind, or hands the exception back unchanged and the run's
answer shows its message.
The operator's verbs, run on the machine the node runs on — the node's own human door, beside the client's approval.
Turns a script's code into a loaded module tree, or into a refusal that names what is wrong with it.
The text scripts guide renders — the script contract and the batteries a
script is handed — read at the call from the docs the release keeps.
Owns the VM's script module trees: every compile and every purge goes through this process, and it remembers what each one produced.
A promised package's own documentation, rendered from this release — what
scripts info <name> answers.
One run — one execution of one action of one accepted script, whoever started it — and what is checked before it, the process it happens in, and the shape of every answer that comes back.
An Elixir module stored as a row in the node database — its code, the hash of
that code and the hash this node has accepted — compiled into the VM and
reachable through the scripts tool and by the node itself; Script.<Name>,
named after its module.
One throttle on this node: the process that holds its bucket, its breaker and the requests waiting on it — and the two request steps that put a request naming it through that process.
A throttle's token bucket as a value: a rate per minute, a burst, the tokens it holds and the instant it last counted them.
A throttle's refusal of a request — the exception YmerNode.Script.Context.request/2
answers as {:error, exception} when a request never left the node.
The values a script resolves by name — one file of NAME=value lines,
outside every database.
Mix Tasks
Runs the browser service's own tests — node --test in browser-service/,
against the real Chromium its pinned Playwright drives.
Build the release image and tag it with the commit it was built from.
A subprocess's output reaching the terminal as it is produced — faithful
for valid UTF-8, with anything unreadable shown as �, and unable to stop
the task that runs it, whatever bytes it carries.
Proves the way a script author's own repository takes the node: a project
depending on this checkout under runtime: false, where none of the
node's applications start, tests a script through YmerNode.Script.Test
and starts the run tree through YmerNode.Script.Harness.
Deploy: move the checkout's committed main into the install — name the
rollback target, build, carry the compose file, recreate the install's
container on the image, and verify it came up on it.