/\_/\
( ¬‿¬ )──●──●──●
c c
An executable canvas: a hierarchical, node-based document where nodes hold Markdown, and code blocks inside that Markdown can be run — with their output optionally cached right next to the code.
Status: early bootstrap. This document is itself a valid meshfox canvas — every ## section here is a node, nested under this root. The leading HTML comment above the title is the meshfox:canvas marker (see SPEC.md, or browse this file with meshfox view — "File format" below includes it live); it's what lets tooling recognize this as a canvas despite the plain .md extension.

- A project is one canvas: a tree of nodes starting from a single root node.
- From the root, large section nodes branch off (e.g. one section per feature).
- From sections, further block nodes branch off. Blocks hold Markdown.
- Markdown inside a block can contain fenced code. A fence can be marked runnable; running it executes the code and (optionally) writes the result back into the same node, right under the code, so nobody has to re-run it just to see what happened last time.
- Two ways to interact with the same files:
- a browser UI (canvas view + block runner) backed by a small Rust server — opens read-only, so pulling up a canvas to look around never one-click-modifies it: running a block is always allowed (you're still explicitly clicking "run", and it's the whole point of a canvas), but a
cached block's output isn't written back to the file until an explicit "Edit" button is clicked, which also unlocks dragging, resizing, and saving layout. Output streams into the browser live as the block runs (not just once it's finished), and a running block gets a Kill button, for when one hangs. - a CLI that runs blocks non-interactively,
make-style, for use in scripts/CI —meshfox listprints every runnable block as a tree, so there's no need to go spelunking through the file to find out what's runnable.meshfox runstreams output live too (the same async, killable executor as the browser UI); Ctrl+C kills whichever step is currently running, whole process group and all, and stops there — whatever earlier steps in the chain already completed stays cached.
- a browser UI (canvas view + block runner) backed by a small Rust server — opens read-only, so pulling up a canvas to look around never one-click-modifies it: running a block is always allowed (you're still explicitly clicking "run", and it's the whole point of a canvas), but a
meshfox canvas format
Reference spec for .canvas.md. Also available at any time via meshfox spec.
A canvas is a Markdown outline: heading nesting is the node tree; bookkeeping (id, position, extra edges) lives in HTML comments, invisible to any normal Markdown viewer. Model comes from JSON Canvas (jsoncanvas.org); this format moves the same tree/graph-of-Markdown-nodes model into plain Markdown instead of JSON, for readable diffs and hand-editability.
File structure
#(H1) — exactly one per document. Content up to the first##is the root node. Always a node, no marker needed (it's the only H1).##,###, ... — a heading is a node only if immediately followed by a<!-- meshfox:node ... -->comment. Without one, the heading (and everything under it) is just prose inside its enclosing node — headings can be freely used for sub-structure without fragmenting the canvas. A node's parent is the nearest enclosing shallower node heading (or root) — unless overridden byparent=(below).<!-- meshfox:node ... -->— right after a heading line. Turns the heading into a node and holds its bookkeeping askey="value"attributes:id,type,x,y,w,h,color,tags,parent. All optional; a bare<!-- meshfox:node -->is enough.iddefaults to a slug of the heading text; only write it explicitly for a stable handle that survives renames (e.g. because an edge references it). First write-back (running a cached block, saving layout) pins the id used, so identity is stable afterward.tags="a,b,c"is a comma-separated list of free-form labels — purely descriptive (no structural meaning), shown as small chips on the node in the web UI.parent="other-id"overrides the heading-nesting-implied parent — needed once a subtree is already######(H6, CommonMark's ceiling: headings can't nest any deeper), where a further child has nowhere left to go but another######heading, which plain heading nesting alone would read as a sibling rather than a child. Nesting past H6 keeps working — every deeper node just keeps writing######and disambiguates withparent=instead of heading depth. Written automatically ("add child" in the UI reaches for it exactly when it's needed); hand-editing it is only for restructuring an already-flattened deep subtree.<!-- meshfox:edge from="other-id" ... -->— one per line, right after a node'smeshfox:nodeline. Declares an extra incoming edge from another node, for graphs that aren't a clean nesting tree. Any number allowed, in addition to the one implicit nesting-parent edge. Besidesfrom, an edge line accepts the same kind of optional styling attributes the web UI's on-canvas edge editor writes:label(arrow text),color(hex or a"1"–"6"preset, same palette as a node's owncolor),style("solid","dashed"— the default look when omitted — or"dotted"),arrowStart/arrowEnd("none"or"arrow"— an edge with neither set gets an arrowhead only atarrowEnd, matching the pre-styling default), andtags(comma-separated, same convention as a node's own). All are omitted from the line unless explicitly set — a plainfrom="other-id"with nothing else is exactly the old, pre-styling form. These extra (meshfox:edge) edges render as a curved connector in the web UI, distinct from the structural nesting tree's right-angle routing.<!-- meshfox:canvas -->— optional, first line of the file. Marks a plain*.mdfile as a canvas even though it isn't named*.canvas.md(used so this doubles as auto-discovery hint; not required for parsing — any correctly-structured file parses as a canvas regardless of name).
Node types
type= on meshfox:node picks one of JSON Canvas's four kinds. Defaults to
(and is omitted for) text.
text(default) — freeform Markdown body.group— purely organizational; body must be empty. Children are whatever nests under it structurally (no separate containment mechanism).file/link— body must be exactly one Markdown link and nothing else:[label](target). Afilenode also accepts two optional display attributes on itsmeshfox:nodeline:display="link"(default) ordisplay="code"—codeshows the target's own file content as a read-only, non-runnable syntax-highlighted preview instead of a plain clickable link. The file is read fresh from disk on every view, confined to the canvas's own directory tree (same boundaryincludetargets are resolved within) — never written back.lang="..."— syntax-highlighting language hint fordisplay="code"(e.g.lang="rust"). Optional; when omitted, the language is guessed from the target's file extension. Ignored whendisplayisn'tcode.
<!-- meshfox:node type="file" display="code" lang="rust" -->
[main](./src/main.rs)
link nodes don't support display/code — their target is an external
URL, not something meshfox reads from disk.
include— same one-link body asfile/link, but the target (another.mdor.canvas.mdfile) is spliced in dynamically by whatever consumer resolves includes — never written back to disk.run/fmt/validatesee the bare link, same asfile/link. See "Includes" below.constraint— body must be exactly one```starlarkfence and nothing else — a sandboxed contract over the document tree, evaluated bymeshfox check. See "Constraint nodes" below.
Any other type= value, a non-empty group body, a file/link/include
body that isn't a single link, or a constraint body that isn't a single
```starlark fence is a parse error (meshfox validate catches these,
plus a missing/cyclic/unparseable include target).
Includes
type="include" dynamically splices another file's content into this
node, resolved fresh every time a consumer asks for it (e.g. the server,
before serving GET /api/canvas to meshfox view) — nothing is ever
written into the including file. run/fmt/validate operate on a single
file's raw text and never resolve includes; only validate reaches far
enough in to catch a broken target, a parse error in it, or a cycle.
The target is told apart as a canvas or plain Markdown the same way
auto-discovery already does: .canvas.md suffix, or a plain .md file
that opens with the meshfox:canvas marker.
- canvas target — parsed and spliced in as real children. Every
spliced node's
idis namespaced{include_id}/{original_id}to avoid collisions with the including document (and with any other include spliced in alongside it), and every level is shifted down by the include node's own level. The include node itself becomes agroup. - plain Markdown target — has no meshfox structure of its own, so it
becomes the include node's own body verbatim, except every heading in it
is shifted down (clamped to H6, CommonMark's ceiling) by the include
node's own level — so e.g. the target's top-level
#doesn't read as a second document root once nested. The include node becomestext.
An included file can itself declare includes; those are resolved too, with a cycle (A includes B includes A) reported as an error rather than recursing forever.
Constraint nodes
A type="constraint" node's body is a sandboxed Starlark
contract over the document tree — a way to assert invariants a canvas
should hold (e.g. "every node tagged table has exactly one file child")
as part of the document itself, checked by meshfox check rather than
enforced only by convention. There's no separate "document" object: doc
is simply the root node, and every node — doc included — exposes the
same navigation methods, so a constraint scopes a check to its own subtree
with self.descendants() the exact same way it would reach the whole
document via doc.descendants(). A constraint typically governs the
subtree it's placed in, so a natural place for one is as the parent of
whatever it's checking, e.g. a constraint node sitting above a group of
table-tagged nodes:
#### Table shape
<!-- meshfox:node type="constraint" -->
##### Users
<!-- meshfox:node tags="table" -->
...
for n in self.descendants():
if "table" in n.tags:
files = [c for c in n.children() if c.type == "file"]
if len(files) != 1:
fail(n.id + ": expected exactly one file child, got " + str(len(files)))
The script sees:
doc— the document's root node.self— the constraint's own node, so a script can find its place in the tree without hardcoding its own id.- On every node (both of the above, and any node reached through them):
.id,.title,.type(a string, e.g."file"),.parent(a string, orNonefor the root),.tags(a list of strings) — plain read-only fields..children()— its direct structural children (same treeCanvas::childrenwalks — not extrameshfox:edgeparents)..descendants()— everything in its subtree (children, their children, ...), not just direct children — the usual way to scope a check to "this constraint's own subtree" (self.descendants()) instead of the whole document (doc.descendants())..node(id)— the node with that id anywhere in the document, orNone..nodes_with_tag(tag)— every node in the whole document whosetagsincludestag, regardless of where it's called from. Preferself.descendants()filtered by tag when a constraint should only govern its own subtree; reach for this only when a rule is genuinely document-wide.
fail(msg)— records a violation without stopping the script, so one constraint can report every offending node in a single run instead of just the first. A script that never callsfailpasses.
Beyond these, and Starlark's own built-ins (len, range, string
methods, list/dict comprehensions, ...), the sandbox has nothing: no file
I/O, no network, no way to see any other node's fully-resolved include
tree, and no way to mutate the document — a constraint only ever
reads and reports. Evaluation is resource-bounded (instruction count, call
depth, heap size); a script that times out or errors (syntax error,
unbound name, ...) counts as a failing constraint, with that error as its
one message. Each constraint node gets its own fresh sandbox — nothing
persists between them, and nothing carries over between one meshfox check run and the next.
meshfox check runs every constraint node and reports pass/fail per node,
exiting non-zero if any fails (or if the file doesn't parse — validate's
job, which check implies). Distinct from meshfox validate: validate
asks whether the file parses as a well-formed canvas; check asks
whether the document, once parsed, actually satisfies whatever rules its
own constraint nodes declare.
Runnable code fences
Lives inside a node's Markdown text, as fence-info-string attributes:
cargo build --workspace
name— identifies a fence formeshfox run/output caching; must be unique within its node. Normally required to make a fence runnable — except a node may have one fence with nonameat all, which is runnable too, implicitly named after its own nodeid. A second unnamed fence in the same node makes the omission ambiguous, so neither gets a name (same as any unnamed fence today — not an error, just not runnable). This is what letsmeshfox run/listskip a redundant trailing block-name argument that would just repeat the node's own id — see "CLI" below — and applies the same way to a fence whose explicitnamehappens to already match its node'sid.cache— optional flag (cacheorcache=true); opts into persisting output back into the file.default— optional flag (defaultordefault=true); marks this fence as its node's default block — the onemeshfox run <path-to-node>addresses without a trailing block name (see "CLI" below). A fence whose (implicit or explicit)namealready equals its own node'sidcounts as default too, without needing this flag. At most one block per node may be default (explicitly, implicitly, or one of each) —meshfox validatereports a conflict as an error.deps— optional, comma-separated list of other blocks this one runs after. Each entry is either a bare block name (a block in the same node) ornode-id/block-name(a block in another node, addressed by nodeidthe same waymeshfox:edge from=addresses nodes). Running a block always runs its full dependency chain first, automatically — each dependency's owndepsare resolved transitively, in order, with no block run twice even if several blocks in the chain depend on it. A cycle, or adepsentry naming a block that doesn't exist, is ameshfox validateerror.env— optional, comma-separated list of declaredmeshfox:vars (see "Variables" below) this block wants in its own process environment. Each entry is a bare name (pass the declared variable through under the same name) orlocal=name(expose it under a different name in this block's own environment) — a leading$on the variable-name side is accepted and stripped, but purely cosmetic:env="$X,LOCAL=$Y"andenv="X,LOCAL=Y"mean exactly the same thing. A block with noenv=never resolves or prompts for any declared variable, however many the document declares as a whole — this is what scopes "does running this block need to ask about anything" to the block itself, not the whole canvas. Anenv=entry naming a variable nothing declares is ameshfox validateerror.tty— optional flag (ttyortty=true); this block wants a real interactive terminal instead of the usual captured/streamed output — e.g.bashon its own (a login-style shell), or anything else that reads from its own stdin expecting a real terminal (ssh,git commit's editor, a REPL, a curses UI). Mutually exclusive withcache(meshfox validateerror) — an interactive session isn't the deterministic "exit code plus text"cachesaves/replays. Attyblock may only be adeps=target of anotherttyblock (meshfox validateerror otherwise) — a non-interactive chain auto-running one as a dependency would mean an unrequested interactive step ambushing it; two chainedttyblocks just hand the terminal over twice, back to back, in dependency order (each still running its owndeps=first, same as any other block). See "Interactive (tty) blocks" below for how the CLI and web UI actually run one.
Supported languages: bash (sh is an alias for it). A fence in any other
language never counts as runnable at all — not with an explicit name=,
and not as a node's sole unnamed fence — so an ordinary Markdown
document's own example fences (a yaml config sample, a json snippet,
...) never get mistaken for something to run just because meshfox was
pointed at the file directly (the meshfox:canvas marker/.canvas.md
suffix is only required for auto-discovery — see "CLI" below — an
explicitly-given path still parses whatever heading structure it finds).
cargo build --workspace
cargo test --workspace
Running test here always runs build first.
Interactive (tty) blocks
A tty block (see its flag above) hands its process the real terminal
instead of the captured/streamed output every other block gets — CLI and
web UI each do this differently, since only one of them actually has a
terminal to hand over.
- CLI — before running a
ttystep,meshfox runchecks that both stdin and stdout are an interactive terminal; if either isn't (piped, redirected, CI), it errors out rather than hanging or silently running non-interactively. When they are, the block's process is connected directly to the real terminal (not captured line-by-line the way every other block's output is) — a script canreadfrom the user, runvim, prompt for a password, whatever a normal interactive shell command could do. Earlier steps in the samedeps=chain still run captured/streamed exactly as usual; only thettystep(s) themselves take over the terminal, handing it back once each one exits. - Web UI —
meshfox viewgives the block a real pseudo-terminal (not just piped stdout/stderr, which can't do cursor movement, raw input mode, or terminal-size queries) and streams it to an in-browser terminal, keys typed there going back to the process's stdin. Clicking run on attyblock opens this as a floating panel over the canvas (draggable/collapsible, like the node text editor), rather than filling in the node's own inline output area the way a normal run does. - Either way, output from a
ttyblock is never written back into the file — that's what thecacheconflict (above) rules out. - A real terminal (CLI's own, or the web UI's pseudo-terminal) is a
genuine tty, so the "commands run without a pseudo-terminal" note under
"Cached output" below doesn't apply to
ttyblocks — a tool that auto-detects color support (cargo,git, ...) sees a real terminal and colors its output without needing--color=always.
Variables
Document-scoped configuration values a canvas wants from whoever runs
it — an install prefix, a log level, an API token — asked for once and
then remembered, the same idea as CMake's cached variables or a
./configure step. Declared as <!-- meshfox:var ... --> comments,
only inside the root node's own body — a meshfox:var found in any
other node is a meshfox validate error, not silently ignored, since a
variable is always document-wide, never per-node:
<!-- meshfox:var name="INSTALL_PATH" prompt="Install prefix?" default="/usr/local/bin" -->
<!-- meshfox:var name="LOG_LEVEL" type="select" choices="debug,info,warn,error" default="info" -->
<!-- meshfox:var name="API_TOKEN" secret -->
Attributes:
name— required. What a runnable fence's ownenv=(see "Runnable code fences" above) refers to when it wants this variable — declaring one here doesn't, by itself, put it in any block's environment; that's opt-in per block, see "Consumption" below.type—string(default),int,bool, orselect. Purely a hint for how to prompt (aboolprompts y/n, aselectshows itschoicesas a menu, ...) and how a UI renders an input for it — an incoming value (from--set, the environment, the cache, or a typed answer) is never validated against it; it's just a string either way.prompt— question text to show when asking for a value; defaults tonameitself.default— used if nothing else resolves the variable (see below).choices— comma-separated; required whentype="select".secret— flag (secretorsecret=true). A secret variable is never read from or written to the on-disk cache (see below) and never pre-filled anywhere — the only way to supply one without an interactive prompt is--set/the process environment. It's asked for fresh every single time it's needed.
Consumption (env=)
A meshfox:var declaration on its own does nothing to any block — it's
only a name a fence's own env= attribute can reference (see "Runnable
code fences" above). Only variables a block actually lists in its own
env= are ever resolved or prompted for on its behalf, and only those
end up in its process environment (under whatever local name it asked
for): running a block that declares no env= at all never touches
meshfox:var resolution, however many variables the document as a whole
declares. This is what keeps, say, meshfox run README.md some-unrelated-block
from ever being asked about an INSTALL_PATH that only some other
block in the document actually uses.
meshfox run <path...> <a,b,c> resolves each requested block's (and its
deps= chain's) own env= independently, right before that specific
block runs — not the whole document's variables up front. If the same
variable is referenced by more than one block in a single invocation
(directly or via deps=), it's only ever resolved/prompted for once: the
first block to need it answers the prompt, which is immediately cached,
so every later block referencing the same variable in that same
invocation just reads the cached answer.
Resolution
Resolving one declared variable (for whichever block's env= asked for
it) tries, in order: an explicit override (meshfox run --set NAME=value, or a value submitted through the web UI's form) → the
process environment → the on-disk cache (skipped entirely for secret)
→ the declaration's own default. Whatever isn't resolved by any of
those needs an interactive answer — a terminal prompt for the CLI, a
form for the web UI — which, for a non-secret variable, is then written
to the cache so a later run of any block referencing the same variable
doesn't ask again.
The cache lives at <dir>/.meshfox/<filename>.env, next to the canvas
file itself (e.g. examples/hello.canvas.md ->
examples/.meshfox/hello.canvas.md.env) — a plain NAME=value-per-line
file, meant to be .gitignored, the same way CMakeCache.txt usually
is. It's safe to hand-edit or delete: deleting it just means every
non-secret variable gets asked about again next time some block's env=
needs it.
CLI
meshfox configure [canvas]— the one place that does walk every declared non-secret variable in the whole document, regardless of which (if any) block currently references it — showing its currently-resolved value (cache / env / default) as the prompt's own default, and writing whatever you answer (even if unchanged) back to the cache. The explicit "set these up now" step, the same roleccmake/cmake -Lplays for a CMake cache; nothing else in meshfox requires running it. Requires a terminal; refuses to run (rather than silently do nothing or apply bare defaults) if stdin isn't one. Secret variables are never shown here — asking for one that's never cached and immediately discarded again wouldn't do anything useful.meshfox runresolves each executed block's ownenv=lazily: only a variable that block actually references, and that's still unresolved after the above precedence, gets an interactive prompt, right before that block runs — no separate configure step required, and no prompt at all for a block whoseenv=is empty or already fully resolved.--set NAME=value(repeatable) supplies overrides on the command line, the non-interactive equivalent of answering a prompt — the same flag CI would use in place of a TTY, whichrunotherwise requires whenever some referenced variable is still missing after--set/env/cache/default. A--setvalue is saved to the cache regardless of whether anything in the current invocation actually references it, same ascmake -Dalways updatingCMakeCache.txt.
Cached output
Running a cached block writes/updates a fenced block immediately after the
source, wrapped in markers so re-runs replace just that region:
cargo build --workspace
<!-- meshfox:output name="build" -->
exit code: 0
...
<!-- /meshfox:output -->
Output (live or cached) that contains ANSI SGR color/style escape codes
renders in color in the web UI, both while a block is still streaming and
for a previously-cached block's saved output. Commands run without a
pseudo-terminal, so a tool that auto-detects "not a real terminal" and
disables its own color output (cargo, git, plain ls) prints plain
text here unless it's told to force color (--color=always, or a script
emitting raw escape codes itself).
Minimal example
<!-- meshfox:canvas -->
# Hello Project
<!-- meshfox:node id="root" -->
Project root.
## Tests
<!-- meshfox:node id="tests" type="group" -->
### Smoke Test
<!-- meshfox:node id="smoke-test" -->
```bash name="smoke" cache
echo "hello from meshfox"
```
Addressed as: meshfox run tests smoke-test smoke (node-id path from root,
then the block name).
CLI
meshfox run [--no-deps] <path...> <names>— run one or more comma-separated named blocks reached by walking node ids from the root. Each named block'sdepschain runs first, automatically, in dependency order; a dependency shared by several requested blocks only runs once.--no-depsskips this and runs only the named blocks themselves, in the order given — the CLI equivalent of the web UI's plain "run" button next to "⛓ run chain". If a node has adefaultblock (see "Runnable code fences" above — one explicitly flaggeddefault, or one whose name already matches the node's ownid), the trailing name can be dropped:meshfox run tests smoke-testaddresses that node's default block directly, instead ofmeshfox run tests smoke-test <block-name>, tried first as an ordinary address and only falling back to this when that doesn't resolve, so it never changes the meaning of an address that already worked. Output prints line by line as the block produces it, not all at once after it exits — the exit code, printed last, genuinely isn't known any sooner. Ctrl+C kills whichever step is currently running (the whole process group it spawned, not justbash) and stops there; whatever earlier steps in the chain already completed stays cached on disk.meshfox list— print every runnable block as an indented tree, each with its[cache]/[default]/[tty]/[deps: ...]/[env: ...]flags and a ready-to-pastemeshfox run <path...> <name>— so you don't have to go spelunking through the file to find out what's runnable. A node whose only block is its default gets a single merged tree line instead of a separate one for the node and another for the block; a node with a default block and other blocks besides gets the node-id shortcut printed on its own header line instead, alongside its other blocks' own lines.meshfox fmt [--force]— fill in missingx/y/w/hvia tree-aware auto-layout (--forcerecomputes all;groupnodes are always skipped, their box is derived, never stored).meshfox view [--port] [--no-open]— local web UI, read-only until "Edit" is clicked in the browser. A run's output streams into the browser live, line by line, as it happens, rather than appearing all at once when the block finishes; a running block gets a Kill button, for when one hangs. Attyblock instead opens a real interactive terminal panel — see "Interactive (tty) blocks" above.meshfox validate— parse-only validation (single root, no duplicate ids, no dangling edges, type body rules, no dangling/cyclicdeps=); no execution, no writes. Exit non-zero on error — usable in CI/pre-commit.meshfox check— run everyconstraintnode's Starlark contract (see "Constraint nodes" above) and report pass/fail per node. Exit non-zero if the file fails to parse or any constraint fails — usable in CI/pre-commit alongside (or instead of)validate.meshfox spec— print this specification.
list/fmt/view/validate/check take the canvas path as an optional
positional argument; run takes it as an optional leading argument (recognized by its
.md suffix, since node ids never have one). Omit it and any of them
auto-discover the single *.canvas.md (or marked *.md) file in the
current directory.
Nobody has to type x/y/w/h by hand. There are two independent auto-layout engines now — one on demand in meshfox fmt, one live in the web UI for whatever's still unpositioned — deliberately not required to agree pixel-for-pixel; each leans on inputs the other doesn't have.
- On demand:
meshfox fmtrunscrates/core/src/layout.rs's tree-aware heuristic and actually writes the result into the file (via the usual surgicalset_node_metapatch, one node at a time). The root and its direct children ("sections") read top-to-bottom with just a small nudge to the right, same as a document's title followed by its headings — not yet a real indent. From there down it's a classic indented tree view, the same shape as a file tree or a collapsed outline: each section's own content steps fully to the right of its parent, siblings at a given depth stack vertically, and further nesting keeps stepping right from there. Box size is estimated from each node's content (line count, whether it has a code fence, whether it needs a run-button row) rather than being a flat constant, so a stub and a node with a paragraph plus cached output don't come out the same size. It's a simple heuristic, not a publication-quality tree drawer — adversarial trees might still look rough:
meshfox fmt # fills in position/size only for nodes missing one
meshfox fmt --force # recomputes and overwrites every non-group node
group nodes are always skipped — their box is derived, never stored, whether or not --force is given. Without --force, a node keeps whatever it already has; fmt only fills gaps, so hand-placed (or already-formatted) nodes aren't disturbed by running it again — the second worked example above, for instance, is already fully positioned, so meshfox fmt on it is a no-op.
-
Live in the browser:
GET /api/canvassends exactly what's in the file — no computed suggestion, nosuggestedX/etc. over the wire.web/src/autolayout.tsfills in a box client-side for anything still missing a real position, using the same overall tree shape aslayout.rs(sections top-to-bottom, deeper nesting branching right, siblings stacked without overlap, agroup's box as the bounding box of its resolved members) but with real browser-only inputs instead of a text-length heuristic: root and its direct children share one width,60%of the viewport; everything deeper gets40%, uniformly regardless of how much deeper. Viewport width is read once, when the canvas loads — it doesn't recompute on window resize. Height is never estimated: it comes from React Flow's own measurement of each node's actual rendered content, live — the layout self-corrects as soon as a real measurement lands (and again later, e.g. as a running block's output grows). A depth-≥2 node additionally gets amax-heightcap so one long block can't drag a whole subtree far from its parent; past the cap it scrolls internally (.mesh-node-body's existingoverflow: auto) instead of growing the box further. None of this is ever written to the file just from loading it — same "don't fight the user's own drag" rule as before (seetouchedNodeIdsinApp.tsx), a node only gets its box persisted once it's actually been dragged/resized, ormeshfox fmtgives it a real one.Edit mode's toolbar has an Auto-layout button that clears every non-group node's stored
x/y/w/hin the file outright (POST /api/canvas/clear-layout), reverting the whole document to auto-placed — behind a confirmation dialog, since it can't be undone from the UI. Useful for backing out of a bunch of hand-placed positions and letting the client lay everything out fresh.
Document-scoped config values a canvas wants from whoever runs it — declared once as <!-- meshfox:var ... --> comments in the root node (this document declares exactly one, INSTALL_PATH, right above "Concept" — invisible here since it's an HTML comment, same as every other bit of meshfox bookkeeping). Declaring one doesn't put it in any block's environment by itself, though — a block has to opt in with its own env= fence attribute to actually reference it (env="$INSTALL_PATH", see "Install" below), and only blocks that do ever resolve or prompt for anything: running any other block in this file never asks about INSTALL_PATH, however many blocks elsewhere might use it. Asked for interactively the first time some block's env= actually needs it, then remembered in a local .meshfox/<filename>.env cache (.gitignored, analogous to CMake's CMakeCache.txt) so it's not asked for again by any block referencing it afterward. meshfox configure walks every declared variable up front regardless of env= usage; meshfox run --set NAME=value supplies one non-interactively (e.g. for CI); the web UI shows a small form in place of a prompt, scoped to just the clicked block's own chain, only for whatever isn't already resolved. See SPEC.md's "Variables" for the full writeup, including secret (never cached, always re-asked) and the int/bool/select types.
A few commands, run for real against the installed meshfox binary (see "Development" below for building/installing it). The output below is cached from actually invoking it (see "Cached output" above) rather than typed by hand, so it can't quietly drift from what the CLI does — and it doubles as an end-to-end check of the runnable-block feature itself: this section's own blocks are ordinary name=/cache fences, executed the same way any project's would be.
Run meshfox spec to print the full format specification (SPEC.md,
embedded in the binary at compile time) — not cached here since dumping it
verbatim into this section would nest the whole .canvas.md grammar inside
an example of the format it's describing.
meshfox -h
exit code: 0
CLI and local web viewer/editor for a meshfox canvas. Run `meshfox spec` for the full .canvas.md format specification.
Usage: meshfox [OPTIONS] [COMMAND]
Commands:
run Run one or more named code blocks
configure Interactively resolve every declared `meshfox:var` (see SPEC.md's "Variables") and save the answers to the on-disk cache (`.meshfox/<filename>.env`, next to the canvas file) so `run` doesn't have to ask again. Shows each variable's currently-resolved value as the prompt's own default — press Enter to keep it. Secret variables are never cached, so there's nothing for this to save for them; they're skipped here and asked for fresh at run time instead. Requires an interactive terminal
fmt Fill in x/y/w/h for nodes that don't have them yet, using a simple tree-aware auto-layout (see `meshfox_core::layout`). Never touches `group` nodes — their box is always derived from their children, never stored — and by default leaves any node's position/size alone once it has one, so hand-placed nodes survive a format
create Create a new, empty canvas file: just the `meshfox:canvas` marker followed by a lone root heading (`#`) named after the file itself (its name with a trailing `.canvas.md`/`.md` stripped). Fails if the file already exists — this never overwrites
view Start the local web UI: canvas view, run buttons. Opens read-only — running a block is always allowed, but click "Edit" in the browser to unlock dragging, resizing, saving layout, and persisting a `cache`d block's output back into the file
validate Validate that a file parses as a meshfox canvas — same checks `run`/`fmt`/`view` already do before touching anything (single root, no duplicate ids, no dangling `meshfox:edge` targets, `group`/ `file`/`link` body rules) — without executing anything or writing the file back. Exits non-zero on a parse error, so it's usable as a pre-commit/CI check
check Run every `constraint`-type node's Starlark contract against the document (see `crate::constraint`/SPEC.md's "Constraint nodes") and report which passed. Distinct from `validate`: `validate` checks that the file *parses* as a well-formed canvas; `check` asks whether the document as a whole satisfies whatever rules its own constraint nodes declare (e.g. "every node tagged `table` has exactly one `file` child") — implies `validate` first, since an unparseable file has no constraints to run. Exits non-zero if the file fails to parse or any constraint fails, so it's usable as a pre-commit/CI check alongside (or instead of) `validate`
list Print every runnable code block in the canvas as an indented tree, each with a ready-to-paste `meshfox run <path...> <name>` — so you don't have to go spelunking through the file to find out what's runnable. Same raw-file-only scope as `run`/`fmt`/`validate` (no include resolution)
node Structural edits to individual nodes in a canvas file: add, move, rename, delete, or set a node's body/position/style/edges — the CLI counterpart to the web UI's Edit-mode node operations (the same `mdcanvas` surgical patches `meshfox view`'s `/api/nodes*` routes use), for scripting/CI or whenever a hand-rewrite would risk getting heading depth, sibling order, or dangling-edge cleanup wrong. Every subcommand validates the fully-patched document still parses before writing it back, same as every other mutating command here
spec Print the full .canvas.md format specification (SPEC.md, embedded in this binary at compile time) — the canonical reference for the format, available offline wherever `meshfox` is installed
help Print this message or the help of the given subcommand(s)
Options:
--agent-help Print usage guidance for AI coding agents (when to prefer `node` subcommands over hand-editing, non-interactive `run`, etc.) and exit
-h, --help Print help
-V, --version Print version
Agent Usage:
If you are an AI coding agent, run `meshfox --agent-help` before hand-editing a
.canvas.md file. It covers when to prefer `meshfox node <verb>` over a raw text
edit, how to run non-interactively, and other guidance not covered above.
meshfox node <op> exposes the same per-node surgical patches
(insert_child_node, delete_node, reparent_node, set_node_title,
set_node_body, set_node_meta, set_node_edges, reorder_by_position —
all in mdcanvas) that back the web UI's Edit-mode operations over its
/api/nodes* routes — so a structural change (adding a child at the right
heading depth, moving a subtree without breaking its nesting, deleting a
node without leaving a dangling meshfox:edge behind) can be scripted or
run in CI without going through the browser, and without hand-rewriting
Markdown heading levels yourself. Every subcommand takes an optional
--canvas <path> (auto-discovered like fmt/validate/list when omitted)
and validates the whole patched document still parses before writing it
back — the same validate-before-commit shape every mutating server
handler already uses. As with any other write in this file, running
meshfox validate afterwards is still worth doing: parsing is validated
here, but a deletion or a rename can still leave a deps=/env=
reference elsewhere dangling, which is validate's job to catch, not
node's.
add <parent-id> <title>— insert an empty child node, last in the parent's subtree; prints the new (slugged) idrm <node-id> [--keep-children]— delete a node and its subtree, or (with the flag) just the node, promoting its direct children to its former parentmv <node-id> <new-parent-id>— move a node under a new structural parent in one step (the web UI needs two: link, then promote)rename <node-id> <title>— change heading text only; id, heading level, and body untouchedbody <node-id> [--file <path>]— replace a node's whole body, from a file or stdinmeta <node-id> [--x --y --w --h --color --type --display --lang]— set position/size/style; an omitted flag keeps the node's current value;grouppositions are rejected (their box is always derived, never stored, same asfmt)edges <node-id> [--from <id>]... [--clear]— replace a node's extra (meshfox:edge) parentsreorder— resync sibling heading order in the file to match current x/y, the same resync the server runs on every UI saveshow <node-id>— print a node's parent/children/extra-parents/type/position (read-only)
meshfox node -h
exit code: 0
Structural edits to individual nodes in a canvas file: add, move, rename, delete, or set a node's body/position/style/edges — the CLI counterpart to the web UI's Edit-mode node operations (the same `mdcanvas` surgical patches `meshfox view`'s `/api/nodes*` routes use), for scripting/CI or whenever a hand-rewrite would risk getting heading depth, sibling order, or dangling-edge cleanup wrong. Every subcommand validates the fully-patched document still parses before writing it back, same as every other mutating command here
Usage: meshfox node <COMMAND>
Commands:
add Add a new, empty-bodied child node under `parent-id`, as the last item in its existing subtree (`mdcanvas::insert_child_node`) — same as the web UI's "add child" button. No position is set, so it stays unpositioned — auto-placed by whatever's viewing it (the web UI's own client-side layout, or `meshfox fmt`) — until something gives it a real one. Prints the new node's id: a slug of `title`, de-duplicated against every id already in the file
rm Delete a node. By default the whole subtree goes with it (`mdcanvas::delete_node`), and any `meshfox:edge from="..."` elsewhere that pointed into the deleted subtree is dropped too, so the file can't be left with a dangling reference. `--keep-children` instead deletes just this node, promoting its direct children (and everything under them, untouched otherwise) to its own former parent (`mdcanvas::delete_node_reparent_children`). Refuses to delete the root either way
mv Move a node to a new structural parent (`mdcanvas::reparent_node`). That core function only ever promotes an *existing* extra-parent edge to structural parent — the web UI's two-step dance (drag a new edge onto the node, then promote it) — so this adds the `meshfox:edge from="new-parent-id"` line itself first, making the move a single atomic step from the CLI. Refuses to move the root, or to move a node into itself or one of its own descendants (would make the tree cyclic)
rename Rename a node's heading text, leaving its id, heading level, and body untouched (`mdcanvas::set_node_title`) — a node's id is pinned the first time it's written and never follows later title edits
body Replace a node's whole Markdown body (`mdcanvas::set_node_body`) — what the web UI's in-node editor would send, if it had one yet (see README's roadmap; for now the UI can reposition and run, not edit text). For a `file`/`link` node the body is its one Markdown link (`[title](target)`); a `group` node's body must stay empty. Reads the new body from `--file`, or from stdin if `--file` is omitted
meta Set a node's position/size/style fields (`mdcanvas::set_node_meta`) — `--x`/`--y`/`--w`/`--h` for a manual position/size override (`meshfox fmt` is the usual way to fill these in), `--color`/ `--type`/`--display`/`--lang` for style/type. Any field left unset keeps its current value. `group` nodes never store a position (`fmt` skips them too, deriving their box from their children instead), so `--x`/`--y`/`--w`/`--h` are rejected for one
edges Replace a node's whole set of extra incoming edges (`meshfox:edge from="..."` lines, `mdcanvas::set_node_edges`) — the non-structural, non-nesting cross-references JSON Canvas-style graphs use. The given `--from` list (repeatable) *replaces* whatever was already there, it doesn't add to it; `--clear` removes them all
reorder Reorder every parent's direct children in the file to match their canvas layout (`mdcanvas::reorder_by_position`, sorted by `y` then `x` among ties) — the same resync the server runs on every save from the web UI, exposed standalone for whenever positions changed by hand (or via `node meta`/`fmt`) and the on-disk heading order should catch up to match what's actually drawn
show Print one node's parent, children, extra parents, type, and position/style fields — a read-only lookup, since eyeballing the tree shape directly from the file gets harder the deeper it nests
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
add then show on a scratch copy, so this doesn't touch the tracked example file:
cp examples/hello.canvas.md /tmp/meshfox-node-demo.canvas.md
meshfox node add --canvas /tmp/meshfox-node-demo.canvas.md tests "Regression Test"
meshfox node show --canvas /tmp/meshfox-node-demo.canvas.md regression-test
rm -f /tmp/meshfox-node-demo.canvas.md
exit code: 0
meshfox node add: added "regression-test" under "tests" in /tmp/meshfox-node-demo.canvas.md
id: regression-test
title: Regression Test
type: text
parent: tests
children: (none)
extra parents: (none)
position: x=? y=? w=? h=?
Runs the smoke block in examples/hello.canvas.md for real, updating its cached output in place — the same surgical patch described above, not a simulation:
meshfox run examples/hello.canvas.md tests smoke-test smoke
exit code: 0
==> smoke
hello from meshfox
Sun Jul 26 15:59:41 +04 2026
(exit 0)
fmt on a scratch copy, so this doesn't touch the tracked example file:
cp examples/hello.canvas.md /tmp/meshfox-fmt-demo.canvas.md
meshfox fmt /tmp/meshfox-fmt-demo.canvas.md
rm -f /tmp/meshfox-fmt-demo.canvas.md
exit code: 0
meshfox fmt: placed 0 node(s) in /tmp/meshfox-fmt-demo.canvas.md
A block flagged tty hands its process a real interactive terminal instead of the captured/streamed output every other block gets — for anything that actually needs to talk to a terminal: an editor, a REPL, ssh. See SPEC.md's "Interactive (tty) blocks" for the full writeup, including why it's mutually exclusive with cache (nothing here to freeze into an output block, so — unlike every other example in this section — there's no cached output shown below).
vim
Run it from a real terminal — meshfox run README.md usage usage-tty vim-demo — and it drops you straight into vim editing that scratch file, same as running vim directly would; :wq (or :q!) hands the terminal back same as it always does. meshfox run checks stdin/stdout are actually a terminal before starting a tty block and errors out otherwise, rather than hanging a script or CI job that happens to reach one. The web UI runs the same block over a real pseudo-terminal instead: clicking "run vim-demo" in meshfox view opens it as a floating terminal panel over the canvas rather than filling in the node's own inline output area.
Renders a canvas's node graph — boxes, tags, cached output, and every structural/meshfox:edge connection — as a plain static HTML/CSS/SVG site (no JS, no live server), through a user-supplied Tera template: every *.tera file in --template is rendered with the canvas's data (context key site) and written to --out at the same relative path minus .tera; everything else in the template directory is copied verbatim (CSS, fonts, images, ...). site-template/ in this repo is a real, working template (used for the example below) that also happens to be a decent way to publish a canvas's README as a project page.
A template's own settings live in an optional template.toml right in its own directory (see site-template/template.toml) rather than as static command-line flags — they're a property of that template, not something to repeat on every invocation: base_url, prefixed onto a relative link/target the command doesn't already copy into --out; and icons, a list of <link rel="..." href="..."> tags (exposed to every template as the icons context key) for the page's own favicon/apple-touch-icon set. template.toml itself is read, never rendered or copied into --out. A template with none gets an empty config — no base_url prefixing, no icon tags — same as before this file existed.
meshfox static -h
exit code: 0
Experimental: export a canvas as a static site. Resolves includes (same as `validate`/`view`), turns the canvas's node tree into a recursive `SiteData` (context key `site`) and hands it to a user-supplied Tera template. A node with no real, authored `x`/`y`/`width`/`height` gets no computed position at all — the template renders it as an ordinary nested HTML element and the *browser* lays it out and sizes it from its real content (no pre-computed/estimated pixels to get wrong); a node that does have all four real values keeps rendering at exactly that authored pixel position. A structural (parent/child) connector between two flow-positioned nodes is drawn in pure CSS (they're always DOM-adjacent); everything else — a `meshfox:edge` cross-reference, or a structural edge touching a real-positioned node — is left for a small non-interactive JS pass in the template to measure and draw. Every `*.tera` file in `--template` (except one whose basename starts with `_`, a partial meant to be `{% import %}`ed rather than rendered standalone) is rendered and written to `--out` at the same relative path minus `.tera`; every other file is copied verbatim (CSS, fonts, ...) — except `template.toml` itself, the template's own config file (optional; a template with none gets an empty `base_url` and no `icons`), read from `--template`'s own directory and never copied to `--out`. A local image referenced from a node's Markdown body is copied alongside the output automatically; a `file`-type node's `display="code"` target is read once and inlined into the HTML directly (nothing left to fetch once static). See `site-template/` in this repo for a working example, including its own `template.toml`
Usage: meshfox static [OPTIONS] --template <TEMPLATE> [CANVAS]
Arguments:
[CANVAS] Path to the .canvas.md file. If omitted: auto-discover the single candidate in the current directory
Options:
-t, --template <TEMPLATE> Template directory
-o, --out <OUT> Output directory. Refused if it already exists and is non-empty, unless `--force` [default: site]
--force Overwrite an existing, non-empty `--out` directory
-h, --help Print help
Rendering examples/hello.canvas.md with that template into a scratch directory:
meshfox static examples/hello.canvas.md --template site-template -o /tmp/meshfox-static-demo --force
ls /tmp/meshfox-static-demo
rm -rf /tmp/meshfox-static-demo
exit code: 0
meshfox static: wrote 7 file(s) to /tmp/meshfox-static-demo
apple-touch-icon.png
favicon-16.png
favicon-32.png
favicon.ico
icon-192.png
index.html
style.css
Rust workspace + a small browser frontend:
crates/
core/ canvas model, .canvas.md parsing/rendering (crate::mdcanvas),
tree derivation, Markdown fence scanning, output-block
rewriting, executors (bash, ...), auto-layout
server/ library crate: axum HTTP backend (load/save a canvas file,
execute a block) + the built web/ UI, embedded at compile time
via rust-embed. No [[bin]] of its own — meshfox-cli links it.
cli/ the only binary: `meshfox`. `run`/`fmt` use crates/core
directly; `view` starts crates/server's backend (with the UI
baked in) on localhost, read-only until the browser's "Edit"
button is clicked. One executable, no separate server process
to install or start.
web/ React + React Flow editor: renders the node/edge graph,
in-node Markdown editing, run buttons wired to the server API.
Built once (`npm run build`) and embedded into the `meshfox`
binary — not shipped or loaded separately at runtime.
core is the shared brain; server wraps it in an HTTP API (and owns the embedded UI); cli is the single front door a user actually runs. Every write to the source file — cached output, saved layout — goes through a surgical patch (mdcanvas::set_node_body / set_node_meta) that touches only the node(s) that actually changed, never a full-document reformat.
Requires Rust (stable, via rustup) and Node.js for the web UI.
crates/server embeds web/dist at compile time (via rust-embed), so build the frontend before building anything that depends on it — a fresh clone has an empty web/dist (just a tracked .gitkeep, see .gitignore) and meshfox view will happily start and serve the API, but its UI route will just say the assets weren't built rather than show anything.
cd web && npm install && npm run build # do this first — see above
cargo build --workspace # build core/server/cli into the one `meshfox` binary
cargo test --workspace # run core's unit tests
# run block "smoke" on the node reached via tests -> smoke-test
cargo run -p meshfox-cli -- run examples/hello.canvas.md tests smoke-test smoke
# validate, fmt, and view all take the canvas path the same way
cargo run -p meshfox-cli -- validate examples/hello.canvas.md
cargo run -p meshfox-cli -- fmt examples/hello.canvas.md
cargo run -p meshfox-cli -- view examples/hello.canvas.md # UI + API on :4590, opens read-only,
# launches your browser (--no-open to skip)
# (once meshfox is on your PATH: `meshfox view README.md`, `meshfox run
# README.md usage usage-help help`, etc. `run` recognizes a leading path by
# its .md suffix — node ids never have one — so it stays unambiguous
# alongside its own path/block-name arguments; auto-discovery still works
# everywhere too when the path is omitted)
Working on the frontend itself: the embedded copy is a snapshot from your last npm run build, so for live-reloading UI development run cd web && npm run dev (on :5173) in a second terminal alongside meshfox view (on :4590) — Vite proxies /api to :4590, same as before.
cargo test --workspace runs the Rust workspace's own unit/integration tests — crates/core's parsing (mdcanvas), tree derivation, auto-layout, fence-scanning, dependency-resolution, and variable-handling logic is the bulk of the coverage, each in its own #[cfg(test)] module next to the code it exercises, plus a handful of higher-level tests in crates/server/crates/cli (e.g. the tty websocket path). Pure Rust — no Node.js, no built frontend, no browser needed, unlike the end-to-end suite below. name=d so it's runnable like any other block here, deliberately without cache, same reasoning as "End-to-end tests"/"Release build": the log is per-run noise, not something worth freezing into this file. Flagged default since its block name (run) doesn't match its node id (unit-tests):
cargo test --workspace
web/e2e/ is a Playwright suite that drives the real UI in a real browser against a real meshfox view — not a mocked frontend — because the bugs this suite exists to catch (a dependency badge clipped by overflow: hidden, a highlight's box-shadow eaten by that same overflow: hidden) were only visible in the genuinely rendered, genuinely laid-out canvas; a component-level test wouldn't have seen either. It runs against three fixture canvases (web/e2e/fixtures/*.canvas.md, one each for dependency-chain UI, scroll/pan interaction, and text selection) — deterministic (no date/timestamps) and separate from examples/hello.canvas.md, so test stability never depends on the documentation example's own content. Every test runs the UI in its default read-only mode (never clicks "Edit"), so nothing in the suite ever writes back into a fixture file. Each suite runs against both Chromium and Firefox (chrome-*/firefox-* projects in playwright.config.ts); a small, explicitly-commented handful of assertions are skipped on Firefox where they hit confirmed Gecko-specific limitations (nested-scrollframe wheel-event chaining; drag-selection anchoring under a CSS transform ancestor) rather than anything wrong in this app's own code.
e2e-prep installs web/'s npm dependencies and Playwright's own Chromium and Firefox copies (separate from any browser already on your system) — both idempotent, so re-running once already done costs nothing. run depends on it (deps="e2e-prep"), so the dependency chain always pulls prep in first — no manual first-time step to remember. Both name=d so they're runnable like any other block here, deliberately without cache, same reasoning as "Release build" below: their logs are per-run noise (install/test timings) rather than something worth freezing into this file. run is also flagged default — the one block per node meshfox run <path> can address without a trailing block name — so meshfox run development e2e-tests runs it (and its e2e-prep dependency) directly, without needing to spell out ... e2e-tests run:
cd web
npm install
npx playwright install chromium firefox
cd web
npm run test:e2e
For interactive debugging instead of a one-shot run, use npm run test:e2e:ui (opens Playwright's UI mode — not runnable here, since it doesn't exit on its own).
playwright.config.ts's webServer starts meshfox view itself (via cargo run, rebuilding only if the Rust side changed) — no server needs to be already running, and no separate npm run build step either, since test:e2e runs it as a pretest:e2e hook. Debug builds of meshfox-server read web/dist fresh off disk on every request (rust-embed's debug-embed feature, which would force compile-time embedding even in a debug build, isn't enabled — see its Cargo.toml), so a frontend-only change just needs npm run build again, not a Rust rebuild, between test runs.
An optimized, distributable single binary. name=d so it's runnable like any other block here, deliberately without cache — a full release compile is slow and its build log isn't worth freezing into this file on every run:
cd web && npm install && npm run build
cargo build --workspace --release
echo "binary: target/release/meshfox"
Copies the release binary to $INSTALL_PATH — see "Variables" above, where it's declared. env="$INSTALL_PATH" is what actually pulls it into this block's environment: the first run/configure that reaches a block with this env= prompts for it (default /usr/local/bin) and remembers the answer in .meshfox/README.md.env afterward, so this doesn't ask again on repeat installs — and, since no other block in this document declares env= at all, INSTALL_PATH is never resolved or asked about by anything else here. deps="release-build/release-build" (a cross-node reference — see "Runnable code fences" in SPEC.md — since release-build is a block in a different node than this one) means installing always builds fresh first. This block's own name already matches its node's id (install), so it's the node's implicit default block — no explicit default flag needed, unlike e2e-tests' run above, which needed one since its block is named run, not e2e-tests; both mechanisms are demonstrated in this document. Deliberately without cache, same reasoning as "Release build": the log is per-run noise, not something worth freezing into this file:
mkdir -p "$INSTALL_PATH"
cp target/release/meshfox "$INSTALL_PATH/meshfox"
echo "installed to $INSTALL_PATH/meshfox"
meshfox is MIT-licensed.
MIT License
Copyright (c) 2026 Maksim Dementev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.