User Guide
Overview
vmblu is a model-driven toolkit for building, understanding, and maintaining software created with coding agents.
It serves two roles at the same time:
- guiding the coding agent to implement the application
- enabling the developer to understand and control what is built
The vmblu toolkit consists of:
- Schemas: formal descriptions of application models and related artifacts, including the data exchanged between components.
- Prompts: structured instructions that guide a coding agent through the vmblu workflow.
- CLI commands: utilities for project initialization, source profiling, application generation, verification, testing, and agent integration.
- Graphical editor: an interactive representation of an application's architecture as interconnected nodes.
- System tool (Sysblu): a wider view of the applications, external services, endpoints, and protocols that make up a system.
- Runtime: a message-switching layer between application components that supports inspection, attribution, and runtime policies.
- Agent integration: an explicit capability layer through which agents can use selected application tools, probes, and events.
The toolkit supports development, but the result is a normal deployable application that can run in a browser, on a server, or in another JavaScript environment.
vmblu is intended particularly for applications that are complex or long-lived: applications that developers must still be able to understand, verify, and extend months or years after their initial creation.
Central to vmblu is an explicit architecture:
- What are the building blocks?
- What is each building block responsible for?
- How are the building blocks connected?
- What data do they exchange?
- What is allowed, and what is not?
Coding agents are very good at turning a clear design into working code. They can implement components quickly, handle repetitive work, and make coordinated changes across a codebase. Architecture requires a different kind of judgment. An agent does not automatically know the product priorities, operational constraints, acceptable trade-offs, or future direction of the system.
The developer therefore has a crucial role during the architecture phase. The developer supplies context, guides the division of responsibilities, challenges the proposed nodes and interfaces, checks the data contracts and dependencies, and approves the architecture before implementation proceeds. The agent can propose and refine the design, but the developer remains responsible for its direction and quality.
vmblu supports this collaboration by making the architecture concrete and inspectable before large amounts of code are generated. The model is not a diagram added after implementation: it is the architectural source from which the application structure and generated artifacts are derived and against which the implementation can be checked.
The graphical editor provides an immediate view of an application's internal structure. It lets the developer add or reorganize nodes, connect components, inspect data formats, navigate to source code, and configure runtime and agent behavior. Much of this guide explains the editor, but its basic workflow is deliberately visual and direct.
An application model describes the architecture inside one application. Real systems often contain several applications together with databases, hosted APIs, message brokers, devices, or existing software. Sysblu provides this wider system view. It maps participating applications and services, their public endpoints and transports, and references to models, protocols, source, documentation, builds, tests, and operational material. It complements the application model; it does not replace it or act as a deployment orchestrator. See The system tool for the complete workflow.
Workflow
vmblu is designed around a repeated conversation between the developer, the coding agent, and the model. The agent proposes and implements; the developer provides context, reviews the architecture, and decides when the design is ready for implementation.
This chapter describes that complete working method. The chapters that follow explain the editor mechanics used at each step. Installation and agent-specific setup are covered separately in the installation guide.
The workflow at a glance
A typical vmblu project follows this sequence:
- Establish the application goal and, when relevant, its wider system context.
- Record the application prompt through the root node in the editor.
- Propose, or ask the agent to propose, an architecture for the application.
- Review the model, the nodes, interfaces, contracts, etc.
- Have the coding agent generate, verify, run, and test the application.
- Review the result
Approval is a milestone, not a permanent freeze. The steps can be repeated as often as the project requires.
Start with direction, not a complete chat prompt
To work effectively on a project, the agent eventually needs three pieces of information:
- that it should use vmblu;
- what application it should build;
- when relevant, the wider system context in which the application operates.
These do not all have to be supplied in one chat prompt. Start with a short, operational instruction that lets the agent create or detect the project:
Use vmblu for the application in ./my-application and initialize
the project if necessary.Initialization creates the project folders, the root entrypoint my-application.blu, and the initial, empty, architecture model in model/my-application.mod.blu.
Next, open the root entrypoint in the editor and use Application prompt to describe the application's purpose, users, important behavior, constraints, and acceptance criteria. Save the model before returning to the agent. For a non-trivial prompt, the editor stores the text in a model-owned Markdown file and records a promptRepo reference on the root node.
Then ask the agent to use that prompt to create the first architecture proposal:
Read the application prompt referenced by the root node.
Propose the architecture in the vmblu model, but do not implement it yet.
Stop when the architecture is ready for review.It is often very helpful to guide an agent in the search for an architecture. Coding agents might become better at it, but unfettered creativity does not always lead to the strongest results.
Not every interactive discussion needs to be copied into the application prompt. However, update it when the application's goals, constraints, or important behavior change so that future agent sessions receive the current intent. The application prompt records durable product intent, the vmblu model is authoritative for the architecture, and the source code contains the implementation.
For a larger system, identify the application being designed and point the agent to the active Sysblu model if one exists. Simple external context can be included in the application prompt; a multi-application system or a project with formal external protocols benefits from an explicit system model. This prevents the application from being designed without its external responsibilities and constraints.
Understand the different prompts
vmblu uses several kinds of prompt, each with a distinct purpose. Keeping them separate avoids turning one large text file into a mixture of workflow rules, product requirements, and component details.
The project-local vmblu prompt
.vmblu/vmblu.prompt.md is created by vmblu init. It tells a coding agent how to work with this particular vmblu project: which entrypoint to use, where the model and node source live, which artifacts are generated, and whether the project has local overrides or unusual constraints.
It is a workflow and project-layout file. It is not the application specification, the architectural model, or the place for detailed product requirements. The agent reads it before making project changes so it can find and handle the real sources correctly.
The application prompt
The application prompt describes the purpose of one application: its users, responsibilities, important behavior, constraints, quality requirements, and acceptance criteria. Create or edit it from the Application prompt action in the graphical editor.
The application prompt belongs to the root node. For a non-trivial model, the editor stores the content in a model-owned Markdown prompt file and keeps a promptRepo reference on the root node. This keeps the structural model readable while ensuring that the prompt remains part of the architecture.
The application prompt should explain intent that is not already expressed by nodes, pins, contracts, types, and connections. Those structural model fields remain authoritative for the architecture.
Node and pin prompts
The architecture describes a system as a series of connected nodes.
Each node can have its own prompt. These prompts are not always required, the role of a node might be obvious, and are usally written by the agent. A node prompt explains the component's responsibility, behavior, constraints, and implementation expectations. It gives the agent focused context when implementing or changing that node.
Pins can also have short prompts explaining when an input is handled or when an output is emitted. Pin prompts are also mostly written by the agent and complement the formal contract.The editor keeps the node and pin prompts together in the node's prompt repository.
Via the node properties, you can also add for each node specific tests that the node needs to pass.
The system-wide prompt
When several applications form one system, system-wide goals and constraints do not belong in any single application prompt. Store them in a separate Markdown document and add it to system/active.sys.blu as the system-level reference with kind prompt.
The active Sysblu model then provides the shared context, while each application model retains its own prompt and internal architecture.
Use Sysblu when the application:
- communicates with another application;
- implements or consumes a public API or message protocol;
- depends on a database, queue, hosted service, device, or existing system;
- participates in a larger build, deployment, test, or operational workflow.
The conventional active configuration is:
system/active.sys.bluWhen you open the file you will find a simple graphical overview of the system environment of an application, for example:

It can group the participating vmblu and non-vmblu applications, their public endpoints, transports, protocol documents, the system prompt, and references to models, source, documentation, builds, tests, and operational material.
Every relationship is explicit in the Sysblu document. Merely placing a prompt or protocol beside active.sys.blu does not attach it to the system. Likewise, an application model does not contain a back-reference to Sysblu, so the same application can participate in more than one system configuration, but the agent will of course be directed to the sysblu model for the application.
Sysblu describes the context between applications; the application model still owns the architecture inside one application. See The system tool for its editor workflow and file format.
Ask the agent for architecture before implementation
The agent's first substantial result should be a model proposal, not a folder full of source code. At this stage, concentrate on semantic architecture:
- Does every node have one understandable responsibility?
- Are important responsibilities missing or combined inappropriately?
- Do group boundaries make the application easier to reason about?
- Are inputs, outputs, requests, and replies attached to the right nodes?
- Are message directions and ownership clear?
- Are payload types sufficiently precise?
- Are dependencies explicit in the connections rather than hidden in source?
- Are external interfaces consistent with the active Sysblu model and its protocol references?
- Are agent-facing tools, probes, and events exposed deliberately and safely?
Coding agents are effective at generating a coherent first proposal, but the developer brings knowledge the agent does not possess: product priorities, organizational boundaries, operational experience, expected change, risk, and acceptable trade-offs. Review the model with that knowledge and ask the agent to split, combine, rename, or reconnect nodes until the responsibilities are convincing.
Several architecture-review loops are normal. Because implementation has not yet started, substantial changes are inexpensive at this stage.
Turn the proposal into a readable diagram
An agent primarily works with the semantic model: nodes, pins, contracts, and logical connections as described by the vmblu schemas. Its first visual arrangement may therefore be rough. A clear diagram is still important because it allows the developer to see boundaries, dependencies, main flows, and unnecessary complexity quickly.
The editor's Auto Layout command uses ELK.js, the JavaScript implementation of the Eclipse Layout Kernel. vmblu uses a layered left-to-right layout with orthogonal routes. Auto Layout works on the currently displayed view, so it can arrange the root model or the contents of an individual group.
Auto Layout repositions nodes and pads, rebuilds routes, and removes existing cables. The editor asks for confirmation, and the result can be undone. It is therefore most useful after an agent has created or substantially changed a model, before detailed manual routing has been added.
After Auto Layout, refine the presentation manually:
- move and resize nodes to emphasize responsibility and flow;
- arrange interfaces and pins in a meaningful order;
- reshape routes where the automatic result is unclear;
- introduce cables to combine related routes and reduce visual noise;
- add labels and aliases where they improve communication;
- arrange the contents of important groups independently.
The semantic architecture is stored in *.mod.blu; visual geometry, routing, and cables are stored separately in *.mod.viz. Save the editor changes before handing the project back to the agent. A vmblu-aware agent treats .mod.viz as a tooling-owned artifact and preserves the developer's manual layout while changing the semantic model. Newly added or removed architecture elements may naturally require another visual review.
Use turn-taking rather than editing the same model concurrently: let the agent finish its proposal, synchronize or reopen it in the editor, review and save your changes, and only then ask the agent for the next revision.
Approve an iteration and implement the nodes
When the developer considers the architecture sufficiently clear, tell the agent that implementation can begin. The agent should:
- read the current application, node, and pin prompts;
- implement model-owned code under
nodes/; - keep shared code intentional and clearly separated;
- respect the node interfaces and message contracts;
- avoid editing generated application, profile, capability, or visual-layout files directly;
- report important deviations or missing architectural information rather than silently hiding them in code.
Architecture approval does not mean every detail must be known. Coarse group nodes and provisional contracts can be refined when implementation reveals new information. What matters is that the current division of responsibilities is understood and consciously accepted.
Let the agent profile, generate, verify, and test
Once source code exists, the agent should normally perform the mechanical validation work. It profiles the source so the editor and agent can relate handlers, transmissions, and types back to the model:
npm run vm:profileIt generates the application and, when capabilities are modeled, its capability manifest:
npm run vm:app
npm run vm:capabilitiesIt runs vmblu verify <entrypoint>.blu to check model and visual compatibility, schemas, the canonical factory index, and the provenance of generated artifacts. It then runs the application's normal build, tests, and development environment.
For model testing, it helps maintain the Markdown scenarios referenced through testRepo, generates deterministic artifacts with vmblu make-test, and runs the selected node, group, or application perimeter with vmblu run-test. The detailed approach is described in Testing with vmblu.
It runs every relevant automated check it can access and reports the commands used, what passed, what failed, and what remains unverified. The developer reviews that evidence, performs any necessary experiential or manual validation, and decides whether the result is acceptable or another design or implementation loop is needed.
Continue changing the architecture
Implementation often exposes better boundaries, missing messages, awkward data contracts, or unnecessary dependencies. Return to the model whenever that happens:
- change the architecture first;
- review the semantic and visual result;
- update the relevant application or node prompts;
- ask the agent to adapt the implementation;
- regenerate derived artifacts;
- verify and test again.
This loop can happen before the first release or years later. vmblu does not assume that architecture stops when code generation begins. Its purpose is to keep the model, the developer's understanding, and the implementation aligned throughout the life of the application.
Editor core concepts
The editor shows the architecture of an application as a visual layout to make that architecture readable. Nodes, pins, interfaces and connections describe how the application is structured and how data can flow.
Nodes
Nodes are the building blocks of a vmblu model. The icon at the left of a node header identifies its type:
- Source node: implemented by source code and identified by the factory icon.
- Group node: contains a submodel but behaves as a single node from outside the group. It is identified by the group icon.
- Linked node: references a node defined in another model or bundle. It is identified by the link or padlock icon.


The type icon also provides access to information specific to that type of node:
- The factory icon shows or sets the source location and factory function or class that creates a source-node instance.
- The group icon opens the internal view of a group node.
- The link icon shows or sets the model and node referenced by a linked node.
- The padlock icon shows the origin of a node imported from a bundle.
Linked nodes are implemented and structurally changed in their own source model. For a linked node, a 'dock node' is stored in the importing file. When the linked node changes in the model it is imported from, the editor can detect and show the modifications in the importing model. The editor then highlights changes so that the importing model can review the these.
The other header icons are common to all node types:
- Gear icon: opens the node properties. One of these are the optional initialization settings, stored as
sx. This JSON data is passed to the node when it is instantiated. - Pulse icon: opens runtime directives, stored as
dx. This JSON data is passed to the vmblu runtime rather than to the node. - Text icon: opens the node prompt. The prompt describes the node's responsibilities, constraints and implementation expectations for the coding agent. Depending on the model configuration, prompt content can be stored inline or through the prompt repository.
Pins and interfaces

Pins are the endpoints through which nodes exchange data. A pin has a name, a direction and, where available, a contract describing its payload.
Input and output pins
- An input pin receives a message and maps to a handler implemented by the source node. For example, the pin
do thismaps to the handleronDoThis(). - An output pin transmits a message produced by the node.
Pin names are case-insensitive: Get File, get file and gET fiLe refer to the same name. Names must be unique among pins in the same direction. Input and reply pins are receiving pins; output and request pins are transmitting pins.
A pin is an endpoint, while a message is the payload sent through that endpoint. The two terms are therefore not interchangeable.
Interfaces
Interfaces group related pins under a shared name. They make a node with many pins easier to understand and provide useful context for both developers and coding agents. A conventional full pin name uses the form interface.pin-name, for example file.get.
vmblu also supports interface-name composition using ., -, _ or + at the start or end of the local pin name. The punctuation determines how the interface name and local pin name are combined. For example, interface file combines with .get, read- and +save as to form file.get, read-file and file save as.
Pins and interfaces can be moved inside a node to improve routing between nodes.
Request and reply pins
Request and reply pins add a reply channel to the normal output/input pattern. A request pin transmits a request and waits for the reply or a timeout. The receiving reply-pin handler returns its result over the back channel. These pins are recognizable by the small circle at the base of the arrow.
A request normally connects to one reply pin. It can additionally connect to regular input pins when another node needs to observe or track the request.
The JavaScript APIs for sending, requesting and replying are covered in Source integration.
Connections and routes
A connection is the semantic link through which messages can flow between compatible pins. A route is the orthogonal, or Manhattan-style, line that represents that connection in the editor.
Connections must have a valid direction, and their contracts must be compatible. Pin names do not have to be identical for a direct connection. The editor prevents directionally impossible connections such as input-to-input connections.

Routes can be reshaped to make a model easier to read. Selecting a pin, interface or node can highlight its connections without changing the model.
Cables
A cable is a shared routing trunk that reduces visual clutter when several connections follow the same path. Pins, group pads and other cables connect to it through cable tacks. A tack can sit on an interior cable segment or directly on one of the two endpoint handles, and multiple routes can share an endpoint.
Tacks determine which messages can pass between a route and the cable:
- Selective tacks connect only to matching pin names or to the explicit alias assigned to the tack.
- Non-selective tacks connect without name matching, while still respecting normal direction and contract rules.
New cable tacks are non-selective by default. Selectivity applies to eligible receiving tacks. A tack alias changes the name visible to the cable without requiring the connected node or pin to be renamed.

Separate cables can be connected through bridge tacks. Connections and highlighting cross a bridge when the tack selectivity rules allow it. A cable cannot bridge to itself, and the editor prevents bridge cycles.
Pins belonging to the same node cannot be connected to each other through a cable.
The gestures for creating, reshaping and converting cables are covered in Using the editor.
Groups and pads
A group presents a submodel as one node. Its external pins are represented inside the group by pads, which form the boundary between the parent model and the group's internal model.
- An input pad brings messages received by a group input into the submodel and connects to compatible receiving pins inside the group.
- An output pad receives messages from compatible transmitting pins inside the group and exposes them through a group output.
Multiple compatible routes can use the same pad, and pads can also connect to cables.

Pads can be created from the group boundary or by extruding a pin from within the group. The editor gestures are described in Using the editor.
Teams
Teams are named, colored categories that make a model easier to read. Names such as ui, data, runtime or agent communicate the role of a node more clearly than color alone and also give a coding agent useful architectural context.
Teams are defined in the model settings. The default team is always present and cannot be removed. A node can be assigned to any team defined in the current model; a node without an explicit assignment uses the default team.
Keep the following rules in mind:
- Each model has its own team list.
- Child nodes do not inherit the team assigned to their containing group. Each node has an explicit team or uses the default team.
- If a team is renamed or removed, existing node assignments are not automatically rewritten. An unresolved assignment is displayed using the default team until the node is reassigned.
- A linked node uses the team assigned to its dock node in the importing model.
Using the editor
The vmblu editor is where you create, review and refine the application architecture. Most actions are available from context menus, while frequently used actions also have keyboard shortcuts. The complete shortcut list is available in the keyboard shortcuts appendix.
All ordinary editing actions participate in Undo and Redo. Use Ctrl+Z to undo and Ctrl+Shift+Z to redo. Save points provide a separate way to return an entire model to a known state and are explained below.
Navigating the canvas
Drag an empty part of the background to pan the current view. Use the mouse wheel to zoom around the pointer position. The recalibrate toolbar action switches between the current zoom and the calibrated view, while grid on/off controls the grid for the top-level view.
Click the group icon in a group-node header to open its internal view. A group view can be moved and resized like a window. Its header controls let you close it, maximize or restore it, recalibrate its contents and toggle its grid. An action performed inside a group view applies to that group's internal model rather than to the parent view.

Main toolbar
The toolbar contains actions that apply to the active model or view.

- Recalibrate switches the top-level view between its current zoom and its calibrated view.
- Grid on/off shows or hides the grid.
- Application prompt opens the root prompt. This is the best place to describe the purpose, requirements and architectural constraints of the application for the coding agent.
- Accept changes accepts changes currently proposed by the editor. These can originate from a changed linked node or from a wire check.
- Wire check compares the drawn routes with the logical connections stored at the current save point. Missing routes are proposed in green, while extra or incorrect routes are shown in red. Review the proposal and use Accept changes when it is correct.
- Sync model reloads the current model, imported models and libraries, then refreshes linked nodes. Use it when a file changed outside the editor or an expected linked-node update has not appeared.
- Set save point stores the current model as a temporary reference state.
- Back to save point restores that reference state and clears the current Undo/Redo history. The model loaded from disk establishes the initial save point.
- Make app generates the JavaScript application from the current model and lets you choose the output path. In a normal coding-agent workflow, the equivalent CLI generation step is usually handled by the agent.
- Settings opens model settings, including the runtime, runtime policy, teams and—when supported by the selected runtime—agent settings.
A save point is not the same as saving the model file. Use Undo/Redo for recent editing steps and a save point before a larger experiment that you may want to abandon completely.

Creating and editing model elements
Right-click the background at the position where you want to add an element. The background menu can create a source node, group node, cable, input pad or output pad. It also provides paste, paste as link, node selection and auto-layout actions.

After creating a node, enter its name and use its header icons to configure its factory or link, initialization settings, runtime directives and prompt. These concepts are described in Editor core concepts.
Working with nodes
Right-click a node to open its context menu.

The available actions depend on the node type and whether the node is linked. Common actions include:
- Highlight routes shows every connection involving the node. Select the node and press
Hto perform the same action. - Add label adds or edits a short visual label. A label is not the node prompt.
- Wider and smaller change the node width without changing its meaning.
- Copy copies the node and its selected context to the editor clipboard.
- Paste pins copies previously selected pins into a local node.
- Source to clipboard copies a source-code outline for a source node to the system clipboard. The coding agent will normally create or update the actual implementation.
- Convert to group node or Convert to source node changes the kind of a local node where the conversion is possible.
- Cut link turns a linked node into a local node that can be changed independently.
- Get from link replaces a local node with a reference to a node stored in another model.
- Save to link saves a local node externally and retains a linked node in the current model.
- Ungroup moves the contents of a group back into its parent view.
- Disconnect removes the node's connections but keeps the node.
- Delete removes the node and its connections.
Linked nodes are structurally read-only in the importing model. Their display name and pin positions can still be adjusted for clarity. Use Cut link before making independent structural changes.
Working with pins and interfaces
Right-click the pin area of a local node to add input, output, request and reply pins or an interface name. The same operations are available through the I, O, Q, R and F shortcuts.

Select a pin or interface and press Enter to edit its name. An input or output pin can be changed to the opposite direction while it is disconnected. Add channel converts an output to a request or an input to a reply; Remove channel performs the inverse conversion.
Pin and interface menus also provide actions to copy, paste, disconnect, delete, move pins to one side of a node, swap sides and show profile or capability settings. Linked-node pins can be moved and inspected, but their structure must be changed in the source model.
Selecting, moving and arranging elements
Click a node to select it. Hold Ctrl and click another node to extend or reduce the selection. Hold Shift and drag on empty background to select the nodes and other visual elements inside a rectangular area.
Pins are selected separately. Hold Shift and drag within a node, over a pin or over an interface to select a range of pins. Hold Ctrl and drag a pin or interface to reposition it. The same gesture moves all pins in a pin-area selection together.
A selection is shown in orange or surrounded by an orange rectangle. Drag the selection to move it while retaining its internal connections.

Right-click a rectangular node selection to open the selection menu.

The menu can align selected nodes left, right or along their top edges; distribute them vertically or horizontally; copy, disconnect or delete them; and autoroute connections between the selected nodes.
Use Group to turn the selected nodes into one group node. Connections to nodes outside the selection remain external group connections. vmblu creates the required group pins and pads and preserves the internal connections. Where necessary, cable geometry is separated between the parent and the new group.
Disconnect retains the selected elements but removes their connections. Delete removes both the elements and their connections. The Clear key disconnects the current node or selection; the Delete key deletes it.
Auto-layout and autoroute
Auto layout uses the Eclipse Layout Kernel (ELK) to arrange the current view. When it is started inside a group view, only that group's contents are laid out.
Auto-layout changes visual geometry, not the logical architecture. It repositions nodes and pads, removes existing cables and rebuilds the connections as direct orthogonal routes. The editor asks for confirmation because this can substantially change a carefully arranged drawing. Use Undo immediately if you do not want to keep the result.

Ana equally valid approach is to do a manual relayout of the proposed geometry. Often a human has a better insight into the abstract architecture and can use that to place the node and connections as she/he sees fit. The 'check connectivity' button on the menu is there to check that if connections were broken during relayout, the diagram is still valid. Missinge connections will be drawn in green, superfluous connections will be drawn in red. With manual layout cables can be used to reduce the number of connections and usually it does not take a lot of time to come to a decent result.

Also after auto-layout, you can reposition nodes, reshape routes and introduce cables manually. These manual layout and routing decisions are stored in the visual model and are preserved when a coding agent changes the application architecture.
Autoroute on a node selection is more limited: it redraws the routes between the selected nodes without moving the nodes themselves. Set a save point first if the existing route geometry is important.
Copying, pasting and linking
Copy a node or node selection with Ctrl+C, then place the pointer at the destination and use Ctrl+V. Repeated pastes are offset so that the copies remain visible.
Use Paste as link or Ctrl+L when the copied node should continue to refer to its original definition. This is useful for reusing a node from another model without duplicating its implementation and prompt. Changes made in the source model are presented for review in the importing model.
Pins and interfaces can also be copied and pasted between compatible local nodes. The editor disables structural paste operations for linked nodes.
Working with routes
Drag from a pin or pad arrow to start a route. Compatible destinations become available while the route is being drawn. Release on a destination to create the connection.
Drag a route segment to reshape it without changing the connection. Hold Shift and drag a route segment or cable tack to reroute from that point; the endpoint nearest to that side is disconnected and becomes the new route end.
Clicking a pin or interface highlights its connections. Select a node and press H to highlight all connections involving that node.
Use Wire check after extensive manual route cleanup. The comparison is made against the logical connections at the current save point: missing routes appear in green and extra or incorrect routes in red. Review the result before choosing Accept changes.
Working with cables
Create a cable through the background context menu or press Ctrl+B. Drag either endpoint handle to reshape its trunk. Tacks attached to a moved endpoint move with it. Hold Ctrl when starting the endpoint drag to extend the cable while leaving its endpoint tacks connected as interior tacks.
Dragging an endpoint tack uses four directional zones around the endpoint. Dragging into the zone that points inward along the cable moves the tack onto the terminal cable segment. Dragging into any of the other three zones keeps the tack attached to the endpoint and changes whether its route approaches from above, below, left or right. The editor reshapes the final route segments automatically and preserves the selected approach when the endpoint or connected pin moves later.
When a route can terminate on a cable, the cable lights up. Release the mouse button on the cable to connect the route through a tack. Releasing within an endpoint marker attaches the tack to that endpoint, including when other routes already share it.
Other useful cable operations are:
- Hold
Ctrland drag a pin-to-pin route segment to convert its middle section into a cable trunk. The original pins remain connected to endpoint tacks by short routes. Very short routes use a short vertical cable with suitably positioned tacks. - Hold
Ctrland drag from a cable segment to start a route from that cable. - Drag a tack to move it along the cable.
- Double-click a tack to add or edit its alias.
- Use the tack context menu to switch an eligible receiving tack between selective and non-selective behavior.

The cable context menu provides the following actions:
- Highlight routes or Remove highlight changes the highlighting for all routes connected through the cable.
- Straight connections straightens the short routes between pins or pads and the cable tacks.
- All selective and All non-selective change all eligible receiving tacks together.
- Convert to routes removes the shared cable trunk and replaces it with individual routes. The action is available only when the equivalent routes can be constructed.
- Disconnect removes every attached route but retains the cable.
- Delete removes the cable and all its attached routes.
Use Convert to routes, rather than Delete, when the logical connections must be retained.
Working with groups and pads
A group can be created directly or from an existing node selection. Click its group icon to work on its internal model.
Adding an input or output pin to the group creates the corresponding pad inside the group. You can also create a pad from the background context menu while working inside the group. To expose an existing internal pin, hold Ctrl+Shift and drag the pin to extrude it to a corresponding group pad.

Double-click a pad, or use Change name from its context menu, to rename it. Disconnect removes its routes while retaining the group pin and pad. Delete removes the pad and its corresponding group boundary pin.
The group-view header menu provides group-level operations, including recalibration and ungrouping. Use the reconnecting form of ungroup when the internal nodes should replace the group while retaining compatible external connections.
Inspecting pin profiles
Select a pin and press P, or choose Profile from its context menu, to inspect the source profile associated with that pin.
For an input or reply pin, the profile shows its contract, the corresponding handler, contract compatibility, description and source location. Clicking the source location opens the implementation at that handler.

For an output or request pin, the profile shows the contract and the locations where the source node emits that message. Each source location can be opened directly.

Profiles let the developer compare the architecture with its implementation. The coding agent normally updates profile data while generating and verifying source code.
Editing tool and event settings
Receiving pins can be exposed as agent tools, and transmitting pins can be exposed as agent events. Open the pin context menu and choose Tool settings or Event settings. The T and E shortcuts open the same settings for the selected pin.
When enabled, a tool is marked with a yellow T and an event with a yellow E in the node. Tool settings include its identifier, title, description, risk, approval policy, timeout, schema, effects, examples and usage guidance. Event settings include its identifier, title, description and schema.


The meaning of tools, events and probes, and the recommended workflow for defining them, are covered in Agent integration.
Context menus at a glance
Right-click the element you want to change. The available menu depends on the element and the current selection:
- The background menu creates elements, pastes content, selects nodes and runs auto-layout.
- The node menu changes a node's presentation, type, link state, contents and connections.
- The pin and interface menus create, arrange, inspect, copy and remove pins and interfaces.
- The selection menu aligns, distributes, groups, autoroutes, copies, disconnects or deletes selected elements.
- The cable menu manages the shared trunk and all its attached routes.
- The cable-tack menu changes tack selectivity and aliases.
- The pad menu renames, disconnects or deletes a group pad.
- The group-view header menu recalibrates or ungroups the current group view.
Context menus show a shortcut next to an action when one is available. Refer to the keyboard shortcuts appendix for the complete list.
The system tool (Sysblu)
A vmblu application model explains what happens inside one application. Real systems usually contain several applications and may also depend on databases, hosted APIs, message brokers, devices, or other software. The system tool, called Sysblu, provides this wider view.
Sysblu is a system map and navigation surface. It shows:
- applications that participate in the system, whether or not they are implemented with vmblu;
- the public endpoints through which they communicate;
- the transport used by each connection;
- links to models, source, documentation, protocols, build scripts, tests, and operational material.
It does not replace the application model and it is not a deployment orchestration language. Applications still own their internal architecture and implementation.
Adding system context to a project
System context is optional. A vmblu application works without it. When a project has a primary system configuration, use this convention:
my-project/
system/
active.sys.blu
prompt.md
chat.protocol.json
client/
client.blu
server/
server.blusystem/active.sys.blu is the discoverable active configuration. Other *.sys.blu files can be kept as alternatives and opened explicitly. The system folder is only an organizational convenience: files in it are not automatically part of the system. Every prompt, protocol, script, or document must be referenced explicitly from active.sys.blu.
Application models do not point back to a system configuration. This keeps an application reusable in several systems.
Opening the system diagram
Open active.sys.blu from the Playground workspace. Playground recognizes the file as a system document and switches to the Sysblu canvas. The canvas uses a black background. Applications implemented with vmblu and non-vmblu applications have different title colors.
The Sysblu menu provides these main actions:
- Add application adds an application card. New applications are vmblu applications by default.
- Application prompt opens the explicit top-level
promptreference. - Save writes the current system document.
Drag a card by its title bar to position it. Use the gear at the left of the title to inspect or delete it.
Applications
An application inspector contains:
- Name: the name shown on the card;
- Role: a short description of its responsibility;
- vmblu application: whether the application is implemented with vmblu;
- References: links to relevant project material.
Clear vmblu application for a hosted API, payment provider, identity service, existing enterprise system, or any other participant that is not a vmblu application. This changes how the application is rendered; it does not create a different node type or open a different inspector.
There is no separate entrypoint property. Add the root .blu file of a vmblu application as a node-level reference with the model kind. The referenced files remain the source of truth; the system document only groups applications and provides a central description of their protocols and connections.
Do not put credentials, tokens, or environment-specific secrets in a system document.
References
A node-level reference has a kind, optional label, and target. The kind is navigation and display metadata; it does not duplicate or type the contents of the referenced file. Available kinds include documentation, model, source, build, deployment, test, operations, and other. The target may be:
- a path relative to
active.sys.blu; - an absolute resource locator;
- an absolute HTTP or HTTPS URL.
Clicking a workspace path opens it in Playground. Clicking a web URL opens it in a browser tab. This makes a hosted API reference such as https://developers.openai.com/api/reference/overview a normal documentation target.
A reference may also define both a command and a working directory:
{
"kind": "build",
"label": "Build client",
"target": "../client/package.json",
"command": "npm run build",
"workingDirectory": "../client"
}Command-capable icons have a small marker. A normal click opens the target; Ctrl/Cmd + click requests the command. Sysblu does not interpret shell syntax or environment variables. The browser Playground has no terminal executor, so actual execution requires a trusted host that supports it.
Adding endpoints
Click the plus sign in a card's title bar to add an endpoint. An endpoint is a named API or protocol attachment point. Its fields are:
- Name: the endpoint or API name;
- Protocol: an optional protocol file, resource locator, or HTTP(S) webpage;
- Role:
client,server, orpeer; - Remarks: optional clarification.
Endpoints do not own additional references. Add specifications, documentation, source, and operational files as references on the containing application. For a hosted API, the protocol can link directly to the provider's API documentation webpage.
Click an endpoint row to edit or delete it. Deleting an endpoint also deletes its connections as one undoable action.
Connecting endpoints
Drag the small connector of one endpoint to another endpoint. The Transport inspector opens before the connection is created. Select a transport such as HTTPS, WebSocket, TCP, UDP, IPC, queue, file, shared store, or unspecified, and add remarks only when useful.
The route label shows the transport. A connection has no separate display name or flow setting: message direction belongs to the endpoint protocol. Click an existing route to edit or delete it.
Protocol documents
When the system owns a formal message protocol, store it as *.protocol.json. The document contains a header, interactions, and reusable message types. A minimal interaction looks like this:
{
"id": "login",
"flow": "right",
"type": "Login",
"response": [{"id": "login-accepted"}]
}flow is right, left, or both. By convention, right means from the client or initiator on the left to the server or listener on the right. response records valid direct correlated replies. Use "any" when every message is allowed, or [] when no reply is expected. It does not describe a state machine.
The top-level types object uses vmblu's normal type representation and may contain shared or recursively referenced named types. The CLI validates both the JSON Schema and semantic references.
Editing and recovery
Adding, editing, moving, or deleting nodes, endpoints, and connections is undoable. Use:
| Action | Shortcut |
|---|---|
| Undo | Ctrl/Cmd + Z |
| Redo | Ctrl/Cmd + Shift + Z or Ctrl/Cmd + Y |
Closing an inspector returns focus to the system canvas, so undo works without an extra click. If a referenced local document cannot be opened, Playground shows a compact dismissible notification and leaves the diagram usable.
A small example
The essential structure of a system document is:
{
"header": {"version": "1.11.0", "name": "Chat system"},
"nodes": [
{
"id": "chat-client",
"kind": "application",
"name": "Chat client",
"vmblu": true,
"position": {"x": 100, "y": 100},
"references": [
{"kind": "model", "target": "../client/client.blu"}
],
"endpoints": [
{
"id": "chat",
"name": "Chat connection",
"role": "client",
"protocol": "chat.protocol.json"
}
]
},
{
"id": "chat-server",
"kind": "application",
"name": "Chat server",
"vmblu": true,
"position": {"x": 500, "y": 100},
"references": [
{"kind": "model", "target": "../server/server.blu"}
],
"endpoints": [
{
"id": "chat",
"name": "Chat service",
"role": "server",
"protocol": "chat.protocol.json"
}
]
}
],
"connections": [
{
"id": "realtime-chat",
"from": {"node": "chat-client", "endpoint": "chat"},
"to": {"node": "chat-server", "endpoint": "chat"},
"transport": "websocket"
}
]
}The Chat project in the vmblu examples repository is the current reference example for the complete format and editor workflow.
Source integration
A vmblu model describes the application architecture; source code implements the source nodes in that architecture. In the normal workflow, the coding agent writes and updates this code. The developer does not have to create every factory, handler and runtime call manually, but should understand the contract between the model and the implementation well enough to guide and review the agent.
The model remains the source of truth. Source profiles, generated applications and capability manifests are derived from the model and source code and should not be edited manually.
Developer and coding-agent responsibilities
The developer is responsible for the architectural decisions:
- defining the responsibilities and boundaries of nodes;
- reviewing pins, interfaces, routes and payload contracts;
- providing application, group, node and pin prompts where additional guidance is useful;
- deciding which initialization settings, external interfaces and runtime policies are required;
- reviewing important implementation choices and the resulting application behavior.
The coding agent performs most of the implementation work:
- creating or updating source files and factory exports;
- implementing a handler for every receiving pin;
- sending messages through the transmitting pins defined in the model;
- following payload contracts and adding useful types and source documentation;
- generating source profiles and other derived artifacts;
- running verification, tests and the project build;
- reporting architectural mismatches instead of hiding them in the code.
Implementation often reveals that a responsibility, contract or connection should change. In that case the agent can propose an architectural update, the developer can review it, and the agent can regenerate the affected source. There is no requirement for the architecture to be frozen before implementation begins.
The source-node contract
Every source node identifies a source file and a factory export. The factory can be a function that returns the node implementation or a class or constructor that creates it. When the application starts, the runtime calls the factory with two values:
txis the node's interface to the vmblu runtime.sxcontains the initialization settings defined for that node.
Runtime directives in dx are interpreted by the runtime and are not passed to the factory. They control concerns such as execution, monitoring and security rather than the node's application behavior.
The factory must provide an object containing the handlers for the node's input and reply pins. The runtime creates all source-node instances before it begins normal message delivery. Messages emitted during initialization are queued until the runtime can deliver them.
The coding agent may choose the source pattern that best fits the project. A generated class might, for example, have this shape:
/** @node file store */
export class FileStore {
constructor(tx, sx = {}) {
this.tx = tx
this.baseDirectory = sx.baseDirectory
}
async onFileRead({ path }) {
const content = await this.readFile(path)
this.tx.send('file.content', { path, content })
}
}This example is a reference for reviewing generated code, not a required template. A function returning an object with the same handlers is equally valid.
Handlers and transmitting messages
Input and reply pins are receiving pins. Each one maps to a handler on the object returned by the factory. The runtime calls the handler with the message payload and sets this to the source-node instance.
The conventional handler name starts with on followed by the pin name converted to Pascal-style words. For example:
file.read -> onFileRead
document saved -> onDocumentSavedThe agent should detect handler-name collisions during implementation. Pin names that differ visually can still map to the same JavaScript handler name.
Output and request pins are transmitting pins. The implementation sends through them using tx. Source code must use the complete model pin name, including any interface-name composition. The agent should not invent an output name in code without adding the corresponding pin to the architecture.
Payloads should follow the contracts defined in the model. Contracts communicate the intended data shape to the developer and coding agent and allow vmblu to check architectural compatibility. TypeScript types or JSDoc can make the same contract visible in source profiles and development tools.
Initialization and runtime configuration
Initialization settings in sx belong to the node's application behavior. Typical examples include a starting value, a file location, a rendering option or a service endpoint identifier. The coding agent should treat the shape of sx as part of the node contract and preserve sensible defaults where values are optional.
Runtime directives in dx are separate. The node implementation should not reproduce runtime scheduling, monitoring or security logic that belongs in the selected runtime. For example, a filesystem request declared in node security settings must still fit inside the model-level runtime policy.
This separation lets the same source node run under different compatible runtimes without mixing application logic with runtime policy.
Keeping the model and source aligned
When asked to implement or update an architecture, the coding agent is instructed to follow this loop:
- Read the vmblu project instructions, entrypoint, model and relevant prompts.
- Inspect existing source, tests and project conventions.
- Implement the factories, handlers and transmissions described by the model.
- Update types and source documentation needed for useful profiles.
- Generate the source profile and application artifacts.
- Run vmblu verification, project tests and the build.
- Report implementation results and any proposed architectural changes.
The agent should not change generated .src.prf, .app.js or capability files by hand. It should change their source—the model or node implementation—and regenerate them.
If source code and architecture disagree, the right resolution depends on intent. An accidental source mismatch should be fixed in the source. A genuine new requirement may justify a reviewed model change followed by regeneration. Multiple model/code review loops are normal.
Source profiles
Source profiles connect the visual architecture back to its implementation. The coding agent generates a profile with:
vmblu profile <project>.bluThe profiler scans the source files referenced by the model. It uses TypeScript information and JavaScript JSDoc to locate handlers, payload parameters and transmitting calls. The resulting .src.prf file is a derived artifact read by the editor.
For a receiving pin, the editor can show its handler, source location, description, parameters and contract compatibility. For a transmitting pin, it can show the places where the source sends or requests that message. These links make it possible to move directly from the architecture to the relevant source.
The profiler normally infers the node and pin from the source structure and handler name. The coding agent can add annotations where the ownership would otherwise be ambiguous:
@node <node name>identifies the model node implemented by a class, function or source section.@pin <pin name>gives the profiler an explicit pin mapping for a handler.- Standard JSDoc summaries and
@paramtags describe JavaScript handlers when TypeScript types are not available.
Node and pin prompts belong in the model or prompt repository. They should not be duplicated into source annotations merely to guide the coding agent.
Generating and verifying the application
After updating the architecture or source, the coding agent normally regenerates and verifies the derived artifacts:
vmblu profile <project>.blu
vmblu make-app <project>.blu
vmblu verify <project>.bluUse the root *.blu entrypoint so the commands resolve the canonical model and project paths. vmblu verify --require-generated <project>.blu can be used when all standard generated artifacts must be present.
The generated application contains the required factory imports, the flattened runtime node list, artifact provenance and runtime options. Depending on the selected runtime, the options can include runtime settings, capabilities and agent configuration. Its startup shape is currently:
const runtime = new Runtime(nodeList, runtimeOptions)
runtime.start()The generated application is build output. Manual edits will be lost the next time it is generated, so changes belong in the model, source nodes or runtime configuration.
The editor can also generate the application through Make app. In a coding-agent workflow, using the CLI is usually preferable because the agent can run generation together with verification, tests and the build.
How the application is started depends on the selected runtime and project tooling. A browser application, Node.js service and agent-enabled application require different entrypoints and supporting infrastructure. The coding agent should maintain the appropriate development and production scripts for the project.
Integration diagnostics
Runtime and profile diagnostics indicate that the implementation and architecture have drifted. The coding agent should investigate them rather than treating them as harmless because the application continues running.
NO HANDLERSmeans a source node has receiving pins, but its factory did not provide a usable implementation object.NO HANDLERmeans a receiving pin has no matching function on the source-node implementation.NO OUTPUT PINmeans the source tried to send or request a pin that is not present among the node's transmitting pins.Not connectedmeans a request pin has no connected target.No channelmeans a request reached no reply-capable destination.Reply timeoutmeans no expected reply arrived within the configured timeout.
The source profile may expose additional mismatches, such as a missing handler, an output with no known emitter or an incompatible payload description. These are useful review signals even when they do not prevent application startup.
Compact tx reference
The coding agent normally handles the runtime API details. This reference is useful when reviewing generated code or diagnosing an integration problem.
| Member | Purpose |
|---|---|
tx.send(pin, payload) | Sends a payload through an output pin and returns the number of target deliveries. |
tx.request(pin, payload, timeoutMs) | Sends through a request pin and returns a promise-like reply handler. The timeout is a number in milliseconds. |
tx.reply(payload) | Completes the request currently being handled by a reply-pin handler. |
tx.next(payload, timeoutMs) | Sends an intermediate response and waits for the conversation to continue. This is an advanced request/reply pattern. |
tx.reschedule() | Places the current message back on the runtime queue. It should be used carefully to avoid an endless retry loop. |
tx.pin | Identifies the transmitting pin that produced the message currently being handled. |
tx.to(nodeName).send(...) | Sends to one named node among the targets already connected to the pin. |
tx.to(nodeName).request(...) | Requests from one named node among the targets already connected to the pin. |
tx.select(nodeName) remains available as an alias for tx.to(nodeName).
Testing with vmblu
Testing a vmblu application combines ordinary code tests with model tests at deliberate architectural boundaries. The coding agent normally creates and runs much of the test code, but the developer decides which behaviour matters, where the useful boundary lies, and what evidence is needed.
vmblu model tests are scenario-based. Their intent is written in Markdown, translated deterministically into a formal test artifact, and executed by a dedicated test runtime. They do not use generated mirror nodes, sequencers, or visual test models.
Use three complementary testing layers
Code tests
Code tests exercise implementation details with the project's normal test framework. They can test functions, classes, components, error handling, and integrations without going through vmblu. The coding agent should create and maintain these tests as it implements a change.
vmblu does not interpret code tests or require a particular test framework. Keep them focused and do not repeat all of them at a model boundary.
Node and group model tests
A node test creates one source node and interacts with it only through its declared pins. It can send messages, make requests, observe sends and replies, mount a returned view, interact with that view, and make DOM assertions. This is the right perimeter for an important node contract, stateful behaviour, error path, or view component.
A group test starts all source nodes below the group with real vmblu routing. The scenario can inject messages only through the group's proxy inputs and observe only its proxy outputs and replies. This tests collaboration while preserving a natural architectural boundary.
An arbitrary selection of unrelated nodes is not a supported test perimeter. If several nodes need to be tested together, give their collaboration an explicit group boundary or test it as part of the complete application.
Application model tests
An application test starts the complete root model with its declared runtime and settings. It proves important workflows at application boundaries rather than repeating every node scenario.
Root-pin interaction and browser interaction are supported by the current foundation. Dedicated drivers for protocols, tools, timers, filesystem events, storage, and similar external boundaries are still future work. Until a driver exists, test such behaviour through an available root or browser boundary, or use the project's normal integration-test tooling.
Keep test intent in a test specification
Model-test intent has a different lifecycle from an implementation prompt. It therefore belongs in a separate Markdown file referenced by testRepo, not in promptRepo or an individual pin prompt.
Every source node or group can have an optional testRepo. Open Node Properties from the node cog to set the test-specification path. The root group's testRepo defines the application tests.
A dock node does not own or override the test specification of its linked definition. Tests of the linked node belong to the model that defines it. Integration behaviour specific to a dock instance belongs in a group or application test in the importing model.
The model can remain valid without any testRepo references. Add model tests where their architectural coverage is useful rather than creating empty test files for every node.
Organize the test files
A typical project uses this layout:
tests/
code/
nodes/
Clipboard.md
Clipboard.test.json
Clipboard.result.json
Orders.md
Orders.test.json
Orders.result.json
Orders/
Validator.md
Validator.test.json
Validator.result.json
app/
application.md
application.test.json
application.result.jsonThe hierarchy under tests/nodes/ follows group and node names. Names identify targets; generated UIDs do not. The root application conventionally uses tests/app/application.md.
The three file types have different ownership:
*.mdis the human-owned test specification and the source of test intent;*.test.jsonis a deterministic artifact generated from the specification and the current model contract;*.result.jsonis the canonical report from the latest run and is normally ignored by Git.
The specification and generated test artifact can be committed. Keep authored fixtures or external-driver code separate from generated files.
Write executable scenarios in Markdown
Each level-two heading defines a scenario. Give it a purpose, one or more actions, and at least one expectation:
# Counter tests
## Adds two values
- Purpose: verify that consecutive inputs update the total.
- Send: `add` = `2`
- Send: `add` = `3`
- Expect send: `total` = `2`
- Expect send: `total` = `5`JSON-looking values are parsed as JSON. Other values are treated as strings. The generated scenario ID is derived from the heading, so Adds two values becomes adds-two-values.
The supported steps are:
| Step | Purpose |
|---|---|
Send | Send a message to an input pin. |
Request | Make a request through a request pin. |
Expect send | Expect an exact message from an output pin. |
Expect reply | Expect an exact reply to a request. |
Wait | Allow asynchronous behaviour to progress for a bounded number of milliseconds. |
Mount | Request a view from a pin and mount it in the browser host. |
Click | Click an element located by CSS or accessible role and name. |
Fill | Fill a located form control. |
Expect view | Assert the count, text, class, or visibility of a located element. |
For example, a framework-independent browser scenario can use accessible locators:
## Logs out from the account view
- Purpose: verify that the mounted view publishes a logout request.
- Mount: `view` = `null`
- Click: `{"role":"button","name":"Logout"}`
- Expect view: `{"locator":{"role":"button","name":"Logout"},"visible":true}`
- Expect send: `logout` = `null`CSS locators are also supported:
- Fill: `{"css":"input[name=email]"}` = `sam@example.com`Use - Timeout: 2000 when a scenario needs a timeout other than the default. Message expectations are compared exactly and in order. Avoid assertions about incidental internal traffic: a scenario should observe only behaviour that belongs to its selected perimeter.
Generate the formal test artifact
Use make-test for the required perimeter:
vmblu make-test node <project>.blu --node "Orders/Validator"
vmblu make-test group <project>.blu --group "Orders"
vmblu make-test app <project>.bluSeveral independent node artifacts can be generated in one command:
vmblu make-test node <project>.blu \
--node "Orders/Validator" \
--node "Clipboard"This is only batch generation. It does not combine those nodes into one test perimeter.
make-test resolves testRepo, parses the executable scenario notation, checks target names and pins, validates the artifact schema, and records fingerprints of both the specification and the model contract. If testRepo is absent or its specification is empty, it writes nothing.
The translation is deterministic. A coding agent can help write the Markdown, but no LLM interprets prose while the artifact is generated or the test runs.
Run the selected perimeter
Run the corresponding artifact with run-test:
vmblu run-test node <project>.blu --node "Orders/Validator"
vmblu run-test group <project>.blu --group "Orders"
vmblu run-test app <project>.bluTo run one scenario while refining or diagnosing it, use the generated scenario ID:
vmblu run-test node <project>.blu \
--node "Orders/Validator" \
--scenario "rejects-empty-order"run-test refuses to run a stale artifact when the specification or model contract has changed. Regenerate it with make-test, review the change, and run it again. A non-passing test produces a non-zero exit status, so the command is suitable for CI.
Each scenario starts with a fresh target. Actions inside that scenario share the same instance, which allows stateful sequences. Node tests create the selected factory directly with a test tx interface. Group and application tests start the routed graph and expose only the selected boundary.
Test browser views
A scenario that uses Mount, Click, Fill, or Expect view automatically uses the browser host. vmblu creates a temporary Vite harness, loads the source through the project's own Vite configuration, and drives a real browser with Playwright. This supports Svelte, React, Vue, plain DOM code, and other Vite-supported sources without putting framework-specific instructions in the test specification.
When no supported system Chrome or Edge browser is available, install the Playwright browser once:
npx playwright install chromiumCanvas, WebGL, and other non-DOM surfaces do not yet have specialized drivers. Use code tests or an application-specific integration test until such a driver is available.
Read the report as evidence
run-test writes *.result.json beside the specification and generated artifact, and prints a concise summary. The JSON report is the canonical result. It records:
- the tested target and perimeter;
- the test artifact and its fingerprint;
- scenario purpose, actions, and expectations;
- observed sends and replies with timing;
- duration, failure evidence, and cleanup or execution errors;
- a status of
passed,failed,skipped, orerror.
The artifact fingerprint prevents an old adjacent result from being mistaken for evidence about a newly generated test. After make-test changes an artifact, its previous result is stale until the test is run again.
Work with the coding agent
A practical model-test loop is:
- identify the behaviour, risk, and most useful node, group, or application perimeter;
- ask the coding agent to inspect the model, prompts, implementation, existing code tests, and test specification;
- let the agent propose high-value scenarios and review their intent;
- add or refine the agreed scenarios in the Markdown specification;
- let the agent run
make-testand review the deterministic artifact; - let it add only the fixtures or integration code the scenarios require;
- run the selected perimeter and inspect the report evidence;
- fix the implementation, scenario, or model contract as appropriate and repeat the affected tests.
The developer owns the perimeter, important behaviour, and acceptance criteria. The coding agent handles most of the mechanics and can identify missing cases, but a large number of generated tests is not a substitute for a deliberate testing strategy.
Current automation boundary
The current foundation supports node, group, and application scopes; direct and routed execution; sends, requests, replies, waits, exact observations, DOM interaction, stale-artifact detection, and structured JSON reports.
The next improvements will add application-boundary drivers in response to concrete use cases. Likely additions include HTTP or WebSocket input, tools, timers, and controlled filesystem or storage events. Combined layer commands, JUnit output, editor actions, and specialized visual drivers may follow later. Until then, use the implemented vmblu perimeter where it fits and retain the project's ordinary testing tools for unsupported external boundaries.
The vmblu runtime
The vmblu runtime turns the architecture in the model into a running application. It creates the node instances, connects their pins, and delivers messages according to the model.
That message switching is the foundation, but the runtime is more than application plumbing. It is a controlled boundary around the application. Depending on the selected runtime, it can attribute and enforce security-sensitive operations and give agents access through explicit capabilities instead of unrestricted application APIs.
This central boundary provides three important benefits:
- application components communicate through one consistent message mechanism;
- sensitive operations can be attributed to the node that caused them and evaluated against policy;
- agents can use a deliberately exposed set of tools, probes, and events without receiving direct access to application internals.
From model to running application
Application generation converts the model into a nodeList containing the factories, pins, and resolved routes required at runtime. The generated application imports the runtime selected in the model and starts it with this structure:
import {Runtime} from '@vizualmodel/vmblu-runtime/rt-base'
const runtime = new Runtime(nodeList, runtimeOptions)
runtime.start()runtimeOptions contains framework compatibility information and, depending on the selected runtime, runtime settings, capabilities, and agent configuration.
When the runtime is constructed, it creates an internal runtime representation for every node and resolves the routes between their pins. Calling start() creates the actual node implementations from their factories and begins message delivery. Messages emitted during node initialization are queued until the runtime can deliver them.
The generated application file contains this wiring and is build output. The developer and coding agent normally work on the model, node implementations, and runtime configuration rather than editing the generated startup code.
One message mechanism
Nodes do not need direct references to one another. A node sends through an output pin, and the runtime delivers the message to the input pins connected by the model. One output may reach multiple targets without the sending node knowing which components receive it.
The same runtime also supports request/reply channels. A node can send a request through a channel pin, wait asynchronously for the reply, and apply a timeout. The runtime tracks the pending request and returns the reply to the correct caller.
This common mechanism is useful beyond decoupling source code. It keeps execution aligned with the visible architecture and gives monitoring, testing, security, and agent access a shared place to observe or control application interaction.
A security boundary
The Node.js safety-enabled runtimes add security attribution around normal message handling. When a node handles a message, the runtime records that node as the active execution context. The attribution follows asynchronous work started by the handler, so a later sensitive operation can still be associated with the node that caused it.
The currently observed areas include:
- file-system reads, writes, and deletes;
- outgoing network access;
- starting external processes.
For each operation, the runtime calculates the effective policy from the application-level security envelope and the node-level request. An allowed operation proceeds, a warning can be reported as a structured security event, and a denied operation is reported and blocked.
This makes the runtime an active security asset: the policy is attached to the architecture, enforcement is centralized, and a security event can identify both the operation and the responsible node. It is still an application-level boundary rather than an operating-system sandbox; deployment permissions, isolation, dependency review, and secret management remain necessary.
The complete policy model and its limits are described in Security enforcement.
A controlled agent gateway
Applications that need agent interaction can select an agent-enabled runtime. These runtimes add a ToolBroker between the agent and the application.
The developer chooses which application operations and observations become capabilities:
- Tools are controlled actions, normally connected to selected input pins.
- Probes are read-only queries of selected application state.
- Events are selected application notifications that an agent may observe.
An agent does not receive general access to node implementations or arbitrary pins. It asks the ToolBroker to use a published capability. The broker checks the capability definition and the selected agent's policy, validates the request, handles approval requirements where configured, and sends an accepted action through the normal vmblu runtime queue.
The resulting message therefore follows the same architecture as an action initiated by a conventional user interface. The application does not need a separate, agent-specific implementation for every operation, and agent access remains visible and reviewable in the model.
The capability model, ToolBroker, agent policies, adapters, and user interfaces are described in Agent integration.
Selecting a runtime
The runtime is selected in the model header. vmblu currently provides four runtime variants:
| Runtime | Intended environment | Node.js security attribution | Agent capabilities |
|---|---|---|---|
@vizualmodel/vmblu-runtime/rt-base | Browser or Node.js | No | No |
@vizualmodel/vmblu-runtime/rt-browser-agent | Browser | No | Yes |
@vizualmodel/vmblu-runtime/rt-als | Node.js | Yes | No |
@vizualmodel/vmblu-runtime/rt-nodejs-agent | Node.js | Yes | Yes |
rt-base is the default and is sufficient for applications that need only message switching. rt-als adds Node.js AsyncLocalStorage attribution and security instrumentation. The two agent runtimes add the capability gateway; the Node.js variant also includes the safety foundation.
The older @vizualmodel/vmblu-runtime/rt-agent name remains available as a compatibility alias for rt-nodejs-agent. New models should use the explicit rt-browser-agent or rt-nodejs-agent name.
Choose the smallest runtime that provides the services the application needs. This keeps deployment requirements clear without giving up the option to add governed security or agent interaction when they are valuable.
Runtime settings
Runtime configuration exists at application and node level.
Application-level settings are stored in header.runtimeSettings. Small configurations can be stored inline. Larger configurations can be placed in a JSON sidecar and referenced from the header:
{
"header": {
"runtime": "@vizualmodel/vmblu-runtime/rt-als",
"runtimeSettings": {
"path": "./my-application.rt.json"
}
}
}The editor exposes these settings from the model settings dialog. It uses the selected runtime to provide an appropriate default structure.
Each node can also contain runtime directives in its dx object. Unlike sx, these settings are interpreted by the runtime and are not passed to the node factory. They are grouped into:
run: execution directives understood by the selected runtime;monitor: observability settings, including message logging;security: the node's requested security policy in safety-enabled runtimes.
Not every runtime implements every directive. In particular, browser runtimes cannot provide the Node.js security attribution used by rt-als and rt-nodejs-agent. The editor adapts the available settings to the selected runtime.
Application settings establish the overall boundary; node settings specialize behaviour inside it. A node can request a narrower security policy, but it cannot broaden the maximum permissions established for the application.
Why the central boundary matters
Without a shared runtime boundary, every component would have to implement its own communication, monitoring, security checks, and agent-facing APIs. Those separate mechanisms would be difficult to compare with the model and easy to apply inconsistently.
vmblu centralizes these cross-cutting responsibilities while leaving business logic in the nodes. The result is not merely a runtime required to move messages: it is the part of the framework that keeps the executing application connected to its architecture and makes security and agent access governable at that same architectural boundary.
Security enforcement
Why runtime enforcement matters
Application code can perform behaviour that is broader than intended. That code may have been written by a coding agent, a developer, or a third-party dependency: it can read or change files, contact the network, or start another process.
Static review and tests remain important, but they cannot describe everything that happens when the application runs. A safety-enabled vmblu runtime adds a second line of defence. It attributes a sensitive operation to the node that caused it, evaluates the operation against the application policy, and allows, reports, or blocks it.
Security policy is therefore part of the architecture rather than a collection of checks scattered through node implementations. A developer can review which nodes need sensitive access without reading every call made by those nodes and their dependencies.
vmblu runtime security is an application-level boundary. It complements dependency review, operating-system permissions, process isolation, secret management, and normal application security; it does not replace them.
Select a safety-enabled runtime
Node.js security enforcement is available in:
@vizualmodel/vmblu-runtime/rt-alsfor applications without agent integration;@vizualmodel/vmblu-runtime/rt-nodejs-agentfor applications that also expose agent capabilities.
When one of these runtimes starts with a model security policy, it activates the security instrumentation automatically and removes it again when the runtime stops. A separate security-reporter node is not required to enable enforcement.
rt-base and rt-browser-agent do not provide Node.js security attribution. Browser applications rely on the browser sandbox, origin rules, and their backend security boundaries.
Security areas and operations
Security settings are grouped into areas containing concrete operations:
| Area | Operations | Scope |
|---|---|---|
| File system | fs.read, fs.write, fs.delete | permitted roots |
| Network | net.egress | permitted hosts |
| Process | process.exec | permitted commands |
Each operation has its own mode:
allow— the operation proceeds when it is inside its configured scope;warn— the operation proceeds and emits asecurity.event;deny— the operation emits asecurity.eventand is blocked.
Allowed operations are silent by default. Applications that need a complete audit trail can enable auditing of allowed operations explicitly. Audit logging changes event visibility, not the policy decision.
There is no global mode that turns every operation into a warning. Policies remain explicit per operation, which lets an application allow network access while denying process execution, for example.
The model security envelope
Security is configured at model level first. This policy defines the maximum envelope for the entire application. Nodes can request permissions within this envelope, but they cannot expand it.
An inline model configuration can look like this:
{
"header": {
"runtime": "@vizualmodel/vmblu-runtime/rt-als",
"runtimeSettings": {
"security": {
"audit": {
"allowed": false
},
"fs": {
"read": {
"mode": "deny",
"roots": []
},
"write": {
"mode": "warn",
"roots": ["./out"]
},
"delete": {
"mode": "deny",
"roots": []
}
},
"net": {
"egress": {
"mode": "warn",
"hosts": ["api.example.com"]
}
},
"process": {
"exec": {
"mode": "deny",
"commands": []
}
}
}
}
}
}This envelope says that the application:
- cannot read or delete files;
- can write only below
./out, with every write reported; - can connect only to
api.example.com, with every connection reported; - cannot start external processes.
Operations omitted from a canonical policy use restrictive defaults. Do not depend on omission when reviewing important policies; keeping the intended operations visible makes the envelope easier to understand.
For a larger configuration, put the runtime settings in a sidecar file and reference it from the model header:
{
"header": {
"runtime": "@vizualmodel/vmblu-runtime/rt-als",
"runtimeSettings": {
"path": "./my-application.rt.json"
}
}
}Relative file roots are resolved from the folder containing the root vmblu model entry point, not from the process working directory. If the entry point is apps/reporting/reporting.mod.blu, the root ./out consistently means apps/reporting/out however the application is launched.
Node security requests
Every node begins with sensitive operations denied. Give a node access only when its implementation requires it.
Node policy is stored in the node's dx.security runtime settings. For example, an image exporter can request warning-level access to a narrower folder inside the application envelope:
{
"security": {
"enabled": true,
"fs": {
"write": {
"mode": "warn",
"roots": ["./out/images"]
}
}
}
}The node implementation does not receive or enforce this policy itself. dx is interpreted by the selected runtime, which keeps security logic separate from business logic.
The editor presents the model envelope, node request, and effective result separately. When a request falls outside the model envelope, it warns that the request will be clipped or denied. The requested settings remain visible so that the developer can correct them deliberately.
Effective policy
The runtime calculates the strict intersection of the model envelope and the node request:
model security envelope
∩
node security request
↓
effective node policy
↓
allow / warn / denyThe effective mode is always the stricter mode:
| Model | Node request | Effective mode |
|---|---|---|
deny | allow | deny |
warn | allow | warn |
allow | warn | warn |
allow | deny | deny |
Scopes are intersected as well. File roots use path containment rather than text equality:
- model allows
./out, node requests./out/images: effective root is./out/images; - model allows
./out/images, node requests./out: effective root is./out/images; - model allows
./out, node requests./private: the scopes do not overlap, so the effective result isdeny; ./out-oldis not considered a child of./outmerely because the text starts the same way.
Network hosts and process commands must likewise be permitted by both policies. A disjoint scope never becomes an empty unrestricted scope; it becomes a denial.
When the model envelope is tightened, vmblu verification and the editor identify node requests that cross the new boundary. The developer can then update those requests knowingly.
Imported nodes
Security permissions are application decisions, not reusable library decisions. When a node is linked from another model, its source implementation and contract can be reused, but its original application permissions are not imported as grants.
The current application must review and configure the linked node's runtime security settings. This prevents a reusable component from silently carrying broad access into a different application.
Runtime security events
Warning and denied operations produce a structured security.event. An allowed operation produces the same event only when allowed-operation auditing is enabled.
A warning event looks like this:
{
"ts": 1710000000000,
"node": "ImageExporter",
"operation": "fs.write",
"detail": {
"path": "./out/images/result.png"
},
"policy": {
"decision": "warning",
"area": "fs",
"action": "write",
"mode": "warn"
}
}A denied operation outside an allowed root adds a reason:
{
"ts": 1710000000001,
"node": "ImageExporter",
"operation": "fs.write",
"detail": {
"path": "./private/result.png"
},
"policy": {
"decision": "denied",
"area": "fs",
"action": "write",
"mode": "deny",
"reason": "fs_root_not_allowed"
}
}The denied event is emitted before the runtime raises a SecurityPolicyError and blocks the operation. Monitoring, tests, and agent-facing infrastructure can consume the security-event stream without being responsible for enforcement.
The runtime uses asynchronous execution context to retain the active node identity across promises, timers, and other asynchronous work started by a message handler. An observed operation performed later by a dependency can therefore still be attributed to the node that initiated the work.
What the runtime observes
The safety-enabled Node.js runtimes instrument supported APIs for:
- file reads, writes, appends, and deletes;
- network requests made with
fetch,node:http, andnode:https; - process creation through supported
node:child_processmethods.
This is meaningful application-level control, but it is not a claim that every possible native operation is intercepted. Direct sockets, native add-ons, worker threads, unsupported filesystem APIs, and operating-system activity outside the runtime process require their own controls. Symbolic links and platform-specific path behaviour must also be considered when defining file boundaries.
Third-party libraries are covered when they use an instrumented API while executing on behalf of a node. If a library uses another path to the operating system, vmblu cannot enforce an operation it does not observe.
Work with the coding agent
The coding agent can inspect node implementations and propose the permissions they require, but the developer should review the security architecture. A practical workflow is:
- select a safety-enabled runtime;
- define the smallest reasonable model envelope;
- ask the agent to identify nodes that perform sensitive operations;
- grant each node only the operations and scopes it needs;
- inspect the effective policies and resolve every clipping warning;
- run the application and security tests, then review warning and denied events;
- tighten the policy when implementation changes remove a requirement.
A coding agent should change the model or node runtime settings rather than embedding policy exceptions in source code. Like other architectural changes, permission changes should be reviewed in the editor before regenerated code is accepted.
Defence in depth
vmblu security controls observed behaviour inside the application process. Use it together with:
- Dependency review — remove unnecessary packages and monitor known vulnerabilities.
- Operating-system permissions — run the process as a user that can access only what it needs.
- Container or process isolation — restrict what the entire process can reach.
- Secret management — provide only required, scoped, and preferably short-lived credentials.
- Browser security — rely on the browser sandbox and keep provider keys and machine-local secrets out of browser code.
- Deployment controls — separate environments and connect security events to appropriate logging and monitoring.
These layers protect the host, process, environment, and supply chain. vmblu adds the architectural layer: it identifies which node initiated an observed operation and applies the policy defined for that part of the application.
Agent integration
Agents, AI nodes, and capabilities
vmblu distinguishes between an AI node and an agent that operates the application.
An AI node is part of the application graph. It performs a defined task such as classification, extraction, summarization, or generation and communicates through normal vmblu pins.
An agent operates outside the application graph. It may be presented through vmblu's browser overlay or connected through an external integration, but it does not call arbitrary application code. It can use only the capabilities deliberately exposed through the vmblu ToolBroker.
vmblu is not an agent framework: it does not prescribe how an external agent plans, reasons, stores memory, or coordinates work. It provides the governed application boundary that an agent or agent framework can use.
That boundary is described with four related concepts:
- Tools are controlled actions, normally bound to selected input pins.
- Probes are read-only queries of selected application state.
- Events are meaningful application notifications, normally bound to selected output pins.
- Effects describe what a tool is expected to change and which probes or events can provide evidence of success.
The developer decides what the application exposes. Everything else remains private, even if the agent knows that the underlying node or source code exists.

Tool and event metadata can be reviewed in the editor:


Design the smallest useful surface
Do not expose every pin. Start from what an external agent genuinely needs to accomplish and verify.
For example:
Let the external agent move the camera to a planet and then verify which body the camera is following.The coding agent can inspect the architecture and propose:
- the camera command input as a tool;
- a request schema and representative examples;
- a probe that returns the camera's current target;
- an event emitted when the target changes;
- an effect linking the tool to that probe or event as evidence.
The coding agent can perform the model and source-code mechanics, including implementing probe(name, args) on the relevant source node. The developer reviews whether the selected surface is appropriate, whether its schemas are safe, and which agent profiles may use it.
This division of responsibility matters. The coding agent understands the implementation well enough to find useful integration points, while the developer decides how much authority an external agent should receive.
Tools, probes, events, and effects
Tools are for controlled application actions. A tool definition includes an ID, description, input schema, risk, approval requirement, timeout, and optional expected effects. Calls with invalid arguments are rejected before a message enters the application.
Probes provide selected state without exposing the complete node object. A source node that owns a probe implements:
probe(name, args)The runtime calls this method only for a declared and permitted probe. Probe arguments and returned values must be serializable and conform to their published contracts.
Events should represent semantic occurrences rather than every low-level update. A renderer may emit an internal camera position every frame, while an agent-facing camera.changed event is emitted only when the tracked object changes. A smaller event surface gives the agent more useful context and reduces noise.
Effects connect actions to evidence. A tool that changes the camera target can name camera.changed and camera.active as the event and probe that verify the change. This lets the ToolBroker distinguish accepting an action from confirming its outcome.
When an agent cannot complete a task because an appropriate capability does not exist, treat that as architectural feedback. Ask the coding agent to propose the missing tool, probe, event, or effect; review the proposal in the model; then regenerate and test the application.
How the ToolBroker governs access
Every agent action passes through the ToolBroker:
agent or agent adapter
→ authenticated agent profile
→ ToolBroker
identity and permission check
argument validation
approval when required
→ vmblu runtime queue
→ target node
→ reply, event, or probe evidenceThe ToolBroker is a policy boundary, not merely a message converter. It:
- accepts requests only for known, enabled agent profiles;
- filters the capability list for that profile;
- denies capabilities that are not explicitly allowed;
- validates tool and probe arguments;
- obtains human approval where required;
- dispatches accepted actions through the normal vmblu message queue;
- captures permitted events and probe results;
- records trace information;
- reports whether an operation was accepted, completed, verified, denied, or failed.
This keeps agent actions inside the same architecture as actions initiated by a conventional UI. Provider protocols can change without changing the internal node implementations.
Agent profiles and permissions
An agent profile defines one controlled view of the application. It contains the agent identity, instructions, interface settings, and explicit allow lists for tools, probes, and events.
Permissions fail closed:
- a missing, unknown, or disabled agent is denied;
- a missing permission set grants no capabilities;
- an empty allow list allows nothing;
- a deny entry overrides an allow entry;
*is available only as an explicit wildcard.
Event permissions apply both when an agent requests an event and when the runtime publishes one. A hidden event cannot enter that agent's event history, trace view, overlay, or LLM prompt.
Several profiles can be stored in one configuration. All enabled profiles are available to the broker and external adapters, but only defaultAgent starts as the embedded runtime agent and browser overlay. Transcripts, events, approvals, and traces remain isolated by profile.
Approval and risk
Consequential tools can require human approval. A pending approval identifies the requesting agent, tool, arguments, risk, and declared effects. The operation is not dispatched until the request is approved.
The browser overlay displays pending approval requests and lets the user approve or deny them. An approval is single-use, expires, and applies only to the exact request shown. The LLM that requested the operation cannot approve its own request.
Use approval for actions that are costly, destructive, difficult to reverse, or externally visible. Approval is not a substitute for permissions or runtime security: the agent must first have permission to call the tool, and the node remains subject to its runtime security policy after approval.
Understand tool results
Agent-facing results use precise states:
| Status | Meaning |
|---|---|
accepted | The runtime accepted the message for delivery; business success is not yet known. |
completed | A request/reply tool returned a reply. |
verified | Declared event or probe evidence confirmed the expected effects. |
unverified | Dispatch occurred, but the required evidence did not confirm success. |
pending | Human approval is required before dispatch. |
denied | Agent policy or approval denied the operation. |
failed | Validation, routing, runtime, or provider execution failed. |
The runtime chooses an appropriate default from the capability contract. Tools with verification metadata wait for evidence, request/reply tools can wait for completion, and intentionally fire-and-forget tools return accepted. An agent must not describe accepted as proof that the action succeeded.
Probes and events are especially valuable here: they give the agent evidence without giving it direct access to node internals.
Select an agent-enabled runtime
Use one of the explicit agent runtimes:
@vizualmodel/vmblu-runtime/rt-browser-agentfor a browser application and optional overlay;@vizualmodel/vmblu-runtime/rt-nodejs-agentfor a Node.js application that also needs security attribution and enforcement.
The older rt-agent name is retained only as a compatibility alias for rt-nodejs-agent. The complete runtime comparison is in The vmblu runtime.
Both agent runtimes add the ToolBroker, capability registry, policy evaluation, probe reads, event capture, and trace recording. The Node.js variant also adds the safety boundary described in Security enforcement.
Configure agent profiles
Open Agent Settings from the model settings dialog. The popup lets you create profiles, select the default embedded agent, configure the browser overlay or supported external projection, and choose allowed tools, probes, and events from the current capability surface.
A browser-overlay configuration can look like this:
{
"header": {
"runtime": "@vizualmodel/vmblu-runtime/rt-browser-agent",
"agent": {
"schema": "https://vmblu.dev/schemas/agents.v1.json",
"version": 1,
"defaultAgent": "operator",
"agents": [
{
"id": "operator",
"type": "overlay",
"enabled": true,
"title": "Application assistant",
"instructions": "Help the user operate the application through published vmblu capabilities.",
"llm": {
"provider": "openai",
"model": "gpt-4.1-mini",
"endpoint": "http://127.0.0.1:8080/v1"
},
"ui": {
"mode": "overlay"
},
"permissions": {
"tools": {
"allow": ["camera.move"],
"deny": []
},
"probes": {
"allow": ["camera.active"],
"deny": []
},
"events": {
"allow": ["camera.changed"],
"deny": []
}
}
}
]
}
}
}Larger configurations can live in <model-name>.agent.json. Reference the sidecar from the header:
{
"header": {
"runtime": "@vizualmodel/vmblu-runtime/rt-browser-agent",
"agent": {
"path": "./my-application.agent.json"
}
}
}Do not place provider API keys in the model, agent sidecar, or browser code. llm.endpoint points to a trusted local bridge or application backend.
The browser agent overlay
The optional browser overlay provides a natural-language interface to the application. The generated application mounts a floating launcher that opens a movable, resizable window with chat and trace views.
The flow is:
user
→ overlay chat
→ embedded AgentRuntime
→ trusted LLM endpoint
→ permitted tool or probe request
→ ToolBroker
→ vmblu runtime and application nodesThe trace view shows policy decisions, tool calls, probe reads, permitted events, approval state, and results. It helps the developer distinguish several common problems: the LLM chose the wrong capability, the profile lacks permission, validation rejected the arguments, the application accepted but did not verify the action, or the runtime blocked a sensitive operation.
The overlay belongs to the embedded AgentRuntime. The ToolBroker remains independent of the UI and can also serve other authenticated integrations.
Generate the agent-enabled application
For normal development, generate the application as usual:
vmblu make-app <project>.bluFor an agent-enabled runtime, application generation also writes the capability manifest and the agent configuration required by the generated app. The generated startup imports these artifacts and supplies them as runtimeOptions; do not edit that generated wiring manually.
Use make-capabilities separately when you want to inspect or validate the capability surface without generating the application, or when an external consumer needs a manifest at a custom location:
vmblu make-capabilities <project>.blu --out ./active.cap.jsonAfter changing tools, probes, events, effects, or permissions, regenerate the application and run vmblu verify. Stale capability or agent artifacts should be treated as errors, not as harmless build output.
External adapter projections
The vmblu capability manifest is provider-neutral. make-agent-adapter can create filtered projections for a selected agent profile:
vmblu make-agent-adapter <project>.blu --target openai --agent operator --out agent.openai.json
vmblu make-agent-adapter <project>.blu --target http --agent operator --out agent.http.jsonCurrent projection targets are:
vmblu— the native, policy-filtered capability view;openai— OpenAI-compatible function definitions for permitted tools and probes;http— a descriptor containing permitted capabilities and proposed HTTP endpoint shapes.
The HTTP artifact is a projection, not a running or authenticated server. An application that exposes it externally must provide a gateway that authenticates callers, assigns a configured agent profile, and sends requests through the ToolBroker. Never accept an arbitrary agentId from an unauthenticated request.
MCP, Claude, LangChain, and other ecosystems can be supported by additional adapters over the same manifest. They do not require a second capability model, and they should not be configured as active targets until the corresponding adapter or gateway is installed and tested.
Browser applications and the LLM bridge
A browser application should not connect directly to an LLM provider with a provider API key. For local OpenAI-compatible development, scaffold the vmblu bridge:
vmblu llm-bridge openai <project-folder>The command creates .vmblu/llm-bridge/ and adds an llm-bridge package script when the project has a package.json. Put OPENAI_API_KEY in the project's machine-local .env.local, review the generated bridge configuration, and start it with:
npm run llm-bridgeThe bridge listens on the loopback interface, accepts the configured browser origin, adds the provider key server-side, and forwards only configured provider paths. Point the overlay profile's llm.endpoint to its local /v1 URL.
The LLM bridge protects the provider credential; it is not the ToolBroker and does not expose the application to an external agent. In production, replace it with the application's authenticated backend while keeping the same separation: browser UI talks to a trusted server, and application actions still pass through a configured agent profile and the ToolBroker.
A practical workflow
For a new agent-facing feature:
- describe what the external agent should accomplish and how success can be observed;
- let the coding agent propose the smallest set of tools, probes, events, and effects;
- review those capabilities in the model and editor;
- assign explicit permissions, risk, and approval requirements to each agent profile;
- select the browser or Node.js agent runtime and configure the interface;
- generate the application;
- test permitted, denied, approval, completion, and verification paths;
- use the trace to refine the capability surface or implementation;
- regenerate and repeat the review loop whenever the architecture changes.
The result is not simply an application with an LLM attached. It is an application with a deliberate, inspectable contract for agent access and a runtime boundary that enforces that contract.
Appendix: Project files and CLI reference
This appendix describes the files, folders, agent resources, and commands that make up a vmblu project. It is reference material rather than a prerequisite for using the editor. To create your first project, start with the installation guide and then follow the workflow.
What is authoritative?
A vmblu project contains both authored source files and generated artifacts. Their roles are different:
- The main
*.mod.blufile and any model files it imports define the architecture. They are the source of truth for nodes, connections, data types, runtime selection, and agent-facing capabilities. - Files in
nodes/contain the implementation owned by model nodes. Developers and coding agents edit these files. - The
*.mod.vizfile stores the editor layout corresponding to the model. It is maintained by vmblu tooling and changes when the diagram is rearranged. - The
*.src.prf,*.app.js, and*.cap.jsonfiles are generated from the model or source code. Regenerate them instead of editing them directly. .vmblu/vmblu.prompt.mdcontains project-local workflow instructions for a coding agent. It does not replace the model or the product specification.- Markdown files referenced through
promptRepocontain authored application, node, and pin prompts. They are part of the model's product and implementation guidance, even though their text is stored outside*.mod.blu. - Runtime and agent sidecars referenced from the model, and files under
system/, are authored configuration. A similarly named file generated next to an application is a derived artifact; its role depends on how the model references it.
Keeping these roles distinct prevents generated files from drifting away from the architecture that produced them.
Default project layout
The following command creates the default layout:
vmblu init <project-name>If vmblu is not available as a shell command, use npx @vizualmodel/vmblu-cli in its place.
Immediately after initialization, the project looks like this:
<project-name>/
<project-name>.blu
package.json
model/
<project-name>.mod.blu
<project-name>.mod.viz
nodes/
.vmblu/
vmblu.prompt.md
overrides/
cache/
logs/Profiling and generation add derived files beside the model. Testing and local agent development can add further subfolders:
model/
<project-name>.src.prf
<project-name>.app.js
<project-name>.cap.json
<project-name>.agent.json
.vmblu/
llm-bridge/The .agent.json artifact is created next to an agent-enabled generated application when agent configuration is stored inline in the model. .vmblu/llm-bridge/ is not created by vmblu init; it appears only when its corresponding command is used.
A project can also contain authored supporting files. Their locations are not fixed, but a typical arrangement is:
prompts/
application.md
nodes/
model/
<project-name>.rt.json
<project-name>.agent.json
system/
active.sys.blu
application-prompt.md
<name>.protocol.json
tests/
code/
nodes/
<node-name>.md
<node-name>.test.json
<node-name>.result.json
app/
application.md
application.test.json
application.result.jsonOnly add these files when the application needs them. References in the model or system document—not their filenames alone—give them meaning.
The root entrypoint
The root <project-name>.blu file is a small JSON manifest:
{
"kind": "vmblu.entrypoint",
"version": 1,
"model": "model/<project-name>.mod.blu"
}Open the entrypoint in the graphical editor or pass it to CLI commands. The editor and CLI resolve its model path to the main architecture file. Using an entrypoint allows the model files to remain together under model/ without making users remember the internal path.
The package.json file
For a new project, vmblu init creates a private ECMAScript-module package with the vmblu runtime as an application dependency and the CLI, TypeScript, and Vite as development dependencies. Run npm install after initialization to install them.
The generated package provides these vmblu scripts:
| Script | Purpose |
|---|---|
npm run vm:init | Initialize missing project files in the current folder. |
npm run vm:profile | Profile the node source files. |
npm run vm:app | Generate the application module. |
npm run vm:capabilities | Generate the capability manifest. |
It also provides the normal Vite dev, build, and preview scripts. A project can adapt these scripts to its application without changing the model layout.
When vmblu init is run in an existing project without --force, it preserves an existing package.json. Install the CLI and runtime explicitly as described in the installation guide.
The model/ folder
The model/ folder is the application model file set.
<project-name>.mod.blu
The main architecture model. It records:
- the schema version and selected runtime;
- imports and factory source paths;
- shared data types;
- groups and source nodes;
- node inputs, outputs, requests, and replies;
- logical connections between nodes;
- node prompts and static settings;
- tools, probes, events, and agent configuration.
A larger architecture can import additional model files. The root entrypoint still identifies the main model from which the complete architecture is loaded.
<project-name>.mod.viz
The visual counterpart of the architecture model. It stores positions, sizes, open or closed group state, routes, cables, labels, and other editor layout information. It must use the same compatibility family as the architecture model.
This file is tooling-owned. Change the layout through the editor and let vmblu save the result.
<project-name>.src.prf
A generated source profile created by vmblu profile. It maps the architecture to handlers, transmissions, and parameter types found in the implementation. The editor and coding agents use it to navigate and compare model and source.
<project-name>.app.js
The generated application module created by vmblu make-app. It contains the compiled nodes, routing tables, runtime import, and provenance of the model that produced it.
Do not change the runtime import or application wiring in this file. Change the model and generate the application again.
<project-name>.cap.json
The generated vmblu capability manifest created by vmblu make-capabilities. It describes the tools, probes, events, policies, and usage guidance exposed to agents. Provider-specific adapters are projections of this manifest; the capability manifest remains the vmblu-native representation.
Agent-enabled application generation also creates the capability manifest next to the generated application and imports it through runtimeOptions.
<project-name>.agent.json
Agent configuration can be stored inline in the model or in an authored JSON sidecar referenced by header.agent.path. When an agent-enabled application is generated from inline configuration, vmblu writes a derived .agent.json file next to the generated application. Do not edit that generated copy; change the model and regenerate it.
When header.agent.path names an existing sidecar, that referenced file is the authored configuration and must be maintained with the model. The generated application imports it rather than replacing it.
<project-name>.rt.json
Larger runtime configurations can be stored in an authored JSON sidecar and referenced by header.runtimeSettings.path. The file can contain run and monitor settings together with the application security policy. Small runtime configurations can remain inline in the model.
Runtime sidecars are configuration, not generated output. Review them as part of the architecture, especially when they grant filesystem, network, or process permissions.
The nodes/ folder
nodes/ contains the implementation code assigned to source nodes. A simple node can use one file, while a complex node or UI component can use a directory. The factory path in the model connects a node to its implementation.
Keep application code allocated to nodes wherever practical. Shared libraries are valid, but they should be an intentional architectural choice rather than a place for code whose ownership is unclear.
Prompt repository files
The root node and individual nodes can use promptRepo to keep their prompts in Markdown files outside the structural model. The editor creates and maintains these references when prompts are saved through the prompt repository.
Prompt files are authored content. Keep the root prompt focused on application purpose, requirements, and architectural constraints. Node prompts describe local responsibilities and implementation expectations. Pin prompts add guidance only where the structural contract is not sufficient. The model's structure, types, connections, capabilities, and runtime settings remain authoritative for executable behavior.
The .vmblu/ folder
.vmblu/ contains project-local vmblu support data, not the main architecture or canonical copies of the schemas.
vmblu.prompt.md
Short project-facing instructions for coding agents. The default prompt tells the agent which root entrypoint to use, where model and node files live, and which files are generated. Add genuine project-specific constraints here, but keep product requirements in the project's specification and architectural decisions in the model.
overrides/
Optional project-local overrides for vmblu behavior or reference material. Use an override only when the project intentionally differs from the installed versioned context.
cache/ and logs/
Local cache and log locations for vmblu tooling. They are operational data and must not be treated as architectural source files.
llm-bridge/
Optional local bridge configuration created by vmblu llm-bridge. The bridge allows browser development to reach a supported LLM provider without exposing the provider API key to browser code. It is local infrastructure, not part of the application model.
The system/ folder
Applications with external interfaces or formal protocols can use system/active.sys.blu as their discoverable system model. It describes the applications, endpoints, transports, connections, and references that make up the wider system context.
Prompts, protocol documents, scripts, and other supporting files become part of that context only when active.sys.blu references them. A protocol stored as *.protocol.json, for example, is not active merely because it is present in the folder. See The system tool for the complete format and workflow.
Test files
Model tests start from an authored Markdown specification referenced by the source node or group's testRepo. The root group's testRepo defines application tests. A conventional layout stores them under tests/nodes/ and tests/app/.
Generate the formal artifact for the required perimeter:
vmblu make-test node <project-name>.blu --node "Orders/Validator"
vmblu make-test group <project-name>.blu --group "Orders"
vmblu make-test app <project-name>.blumake-test writes a deterministic *.test.json artifact beside its Markdown specification. run-test executes it and writes the canonical *.result.json report beside both files. The Markdown specification and generated artifact can be committed; the latest result is normally ignored by Git.
These commands do not create mirror nodes or visual test models. Node tests create one factory directly, while group and application tests execute real runtime routing within the selected natural boundary.
See Testing with vmblu for the testing workflow.
Resources used by coding agents
Agent-assisted work draws on four different kinds of information:
Agent skill
The installed SKILL.md defines the general vmblu workflow: initialize or detect the project, read its local prompt, resolve the entrypoint, load the matching schema context before model changes, and preserve generated artifacts.
Versioned CLI context
The installed CLI contains canonical reference material under cli/context/<schema-version>/. Depending on the schema version, this includes the model, visual-layout, source-profile, capability, system, and protocol schemas together with the model annex.
The context distributed with the installed CLI is canonical. General schemas and framework documentation are not copied into every project's .vmblu/ folder.
Project-local prompt
.vmblu/vmblu.prompt.md describes how the general workflow applies to this project, including entrypoints, local paths, overrides, and special constraints.
Product specification
Files such as application-prompt.md, spec.md, or other requirements describe what the application should do. They provide product intent; the model remains the source of truth for the implemented architecture.
Telling a properly configured coding agent to use vmblu activates this workflow. Installation and update instructions for agent support are in the installation guide.
CLI command reference
Commands that accept a model can use either the root .blu entrypoint or the direct path to the main .mod.blu file. The entrypoint is normally clearer and more stable.
Project and generation commands
| Command | Result |
|---|---|
vmblu init <folder> | Create the default project layout. |
vmblu profile <model> | Generate or update the source profile. |
vmblu make-app <model> | Generate the application module. |
vmblu make-capabilities <model> | Generate the vmblu capability manifest. |
vmblu make-test <node|group|app> <model> | Generate a formal model-test artifact from its Markdown specification. |
vmblu run-test <node|group|app> <model> | Execute a model-test artifact and write its JSON report. |
vmblu verify <model> | Check schemas, compatibility, the canonical factory index, and generated-artifact provenance. |
vmblu migrate <version> [folder] | Move a project to an installed schema/context version. |
vmblu verify reports missing generated artifacts as skipped by default. Add --require-generated when all standard artifacts must exist and be current.
Run migration with --dry-run first. Migration updates schema-version metadata and ensures the current local vmblu folders and prompt exist; it is not a substitute for reviewing behavioral changes between compatibility families.
Agent integration commands
| Command | Result |
|---|---|
vmblu agent list | List agent integrations included with the CLI. |
vmblu agent install <agent> | Install an agent's support files in the user home folder. |
vmblu make-agent-adapter <model> --target <target> | Project vmblu capabilities to a vmblu, openai, or http adapter document. |
vmblu llm-bridge openai [folder] | Scaffold a local OpenAI bridge under .vmblu/llm-bridge/. |
Use --dry-run before commands that install, migrate, or scaffold files. Use --force only after reviewing which existing files will be replaced.
The repository-maintenance command vmblu plugin build-codex assembles the repo-local Codex plugin. It is intended for vmblu contributors, not normal application development.
Run vmblu --help or vmblu <command> --help for the complete command and option list supplied by the installed CLI.
Appendix: Keyboard shortcuts
Keyboard shortcuts apply while the editor canvas has focus. Many actions use the current selection or the last pointer position, so select the relevant node, pin, interface, or other widget before pressing the shortcut.
The editor currently uses Ctrl for modified shortcuts on every platform. Browser or operating-system shortcuts can take precedence when the canvas does not have focus.
Edit the selected item
| Action | Shortcut |
|---|---|
| Add input pin | I |
| Add output pin | O |
| Add request pin | Q |
| Add reply pin | R |
| Add interface name | F |
| Show selected pin profile | P |
| Show selected pin capability | E or T |
| Add or edit node label | A |
| Highlight selected node routes | H |
| Edit selected widget text | Enter or + |
| Clear selected widget text | - |
| Select previous or next widget | Arrow Up or Arrow Down |
| Clear selection | Escape |
| Disconnect selected node or selection | Clear, where available |
| Delete selected node, pin, interface, or selection | Delete |
The Clear key is not available on every keyboard. Use the appropriate context menu action when it is absent.
Create, copy, and resize
Creation shortcuts place the new item at the last pointer position in the active view.
| Action | Shortcut |
|---|---|
| Create source node | Ctrl+S |
| Create group node | Ctrl+G |
| Create cable | Ctrl+B |
| Create input pad | Ctrl+I |
| Create output pad | Ctrl+O |
| Copy selection | Ctrl+C |
| Paste | Ctrl+V |
| Paste as link | Ctrl+L |
| Make selected node wider | Ctrl++ |
| Make selected node narrower | Ctrl+- |
| Undo | Ctrl+Z |
| Redo | Ctrl+Shift+Z |
Select and move
| Action | Gesture |
|---|---|
| Pan the view | Drag empty background |
| Select multiple nodes | Shift+drag empty background |
| Select pins in an area of a node | Shift+drag on a node, pin, or interface |
| Move a pin or interface | Ctrl+drag the pin or interface |
| Extend the node selection | Ctrl+click a node |
| Extrude a pin to a group pad | Ctrl+Shift+drag the pin outside its node |
Draw and edit routes
| Action | Gesture |
|---|---|
| Draw a route | Drag from a pin or pad arrow |
| Reroute from an existing route | Shift+drag a route segment |
| Reroute from a cable connection | Shift+drag a cable tack |
| Convert a route into a cable trunk | Ctrl+drag a route segment |
| Start a route from a cable | Ctrl+drag a cable segment |
Work with cables
| Action | Gesture |
|---|---|
| Create a cable at the pointer | Ctrl+Shift+click empty background |
| Reshape a cable from an endpoint | Drag the cable endpoint |
| Extend a cable while leaving its endpoint tacks on the trunk | Ctrl+drag the cable endpoint |
| Move a cable tack along its cable | Drag the cable tack |
| Change how an endpoint route approaches the cable | Drag the endpoint tack away from the inward cable zone |
Text and menus
| Action | Gesture |
|---|---|
| Edit a text label, pad, or cable-tack alias | Double-click it |
| Open a context menu | Right-click |