Component Canvas
The canvas is the visual editor where you build processing logic by placing component instances, connecting their inputs and outputs, and monitoring live execution. It is the centre of the environment workspace: pick an environment in the rail and its canvas opens.
Adding Instances
The Library tab of the panel to the right of the canvas lists all component types from the active library. To add a component:
- Select Library in the right-hand panel
- Find the component type you want (components are organized by category)
- Drag it onto the canvas
A new instance node appears with the component's default configuration. Each node shows:
- Header: component name and icon
- Input pins (left side): fields that receive data
- Output pins (right side): fields that produce data
You can drag nodes to reposition them on the canvas. Nodes snap to a grid for clean alignment.
The Inspector shares that panel and takes over whenever something on the canvas is selected. Switch back with the Library / Inspector control at the top of the panel. If the panel is collapsed, a floating book button at the top-right of the canvas reopens it.
Configuring an Instance
Double-click a component instance to open it in the Inspector, in the panel to the right of the canvas. If you had collapsed that panel, it reopens itself. On a phone the same settings open as a drawer instead. It carries up to four tabs:
Overview
Shows the instance name and tables of the input and output fields with their current wire connections and values. Edit offline values for input fields here (see Offline Values below).
- Instance Name: a custom name to identify this instance (e.g., "Room 3 Temp Avg")
- Input Fields: each field's live value, offline value, and wire connection
- Output Fields: each field's live value and wire connection
Configuration
Shown only when the component defines config fields. Static settings that don't change at runtime and control how the component behaves. Depending on the component, you may see:
- Number fields: with optional min/max bounds and step size
- Text fields: with optional validation patterns
- Toggle switches: for boolean settings
- Dropdowns: for predefined choices
- File uploads: for components that need a model or data file (see File Fields below)
File Fields
Some components need a file rather than a value — a trained model, a lookup table, a calibration curve. Those appear on the Configuration tab as an upload area that states the accepted file types and the size limit before you pick anything, so you are not discovering the rule by being refused.
Each instance holds its own file. Two instances of the same component can run different models.
Uploading: drop a file onto the area or click to browse. Once a file is in place the card shows its name, size, and version tag, and you can drop a new file straight onto that card to replace it. A replacement takes effect within one scan cycle — the component reloads the file without the environment restarting.
Versions are kept, not overwritten. Every upload for a field is retained and one is active. Open the version history from the card to see them all with who uploaded each and when, download any of them, make an older one active again, or delete one you no longer want. Deleting the active version promotes the most recent remaining upload, so a component that requires a file does not silently lose it while you prune.
A file is attached to a saved instance. Upload is unavailable on an instance you have just placed and not yet saved, because there is nothing on the server to attach the file to yet. Save the environment first.
If a component requires a file and none has been uploaded, the field says so and the instance reports an error naming the field rather than starting and failing obscurely. A file the server refuses — wrong type, too large, sent to a field that is not a file field — is reported on the field with the reason, and nothing is stored.
Files are removed along with the instance that owns them, and along with the component library if you delete that.
Canvas
Controls how the node looks and sits on the canvas:
- Position: the node's X/Y coordinates, snapped to the grid
- Node Width: the visual width of the node in grid units
- Minimal Mode: a compact view without the header and status bar
- Customize Layout...: opens the pin-layout editor (see Per-Instance Pin Layouts below)
Documentation
Shown only when the component type ships documentation. Rendered here.
Connectors
Connectors are the bridge between the canvas and the rest of Koios. They appear as square nodes on the canvas and come in seven types. Input connectors feed live data into a component; output connectors receive a component's result and write it somewhere.
Each connector type only exposes the fields that make sense for it. For example, a Device connector lets you wire status or error_code into a component, but not value, because devices don't have one. The connector picker filters the available fields based on the entity you choose.
Tag Connector
Reads from or writes to a tag.
- Tag: which tag to read from or write to
- Field: which tag field to use:
value,quality,timestamp,status, orerror_code
For output tag connectors, only writable tags (output usage) are listed.
History Connector
Provides on-demand access to a tag's historical data. The connected component receives a history provider that can query the time-series database for trends, rolling statistics, or pattern detection.
- Tag: which tag's history to access
Device, AI Model, Binding, Scan Group Connectors
Each of these reads a curated set of live fields from the named entity. Use them to build components that react to system state. For example, gate control logic on a device's status, or feed a model's current prediction into another component.
- Entity: pick the device, model, binding, or scan group
- Field: the connector picker shows only the fields valid for that entity
Component Connector
Reads an output from another component instance. This is typically used for cross-environment composition, for example a fast control loop in one environment feeding a slower analytics environment. It can also reference a component in the current environment.
- Environment: which environment contains the source component
- Instance: which component instance to read from
- Output Field: which output field to use
Wiring
Wires carry data between component pins and connectors. To create a wire:
- Click on an output pin (right side of a component or an input connector)
- Drag to an input pin (left side of a component or an output connector)
- Release to create the wire
Rules:
- Each component input can have at most one incoming wire
- Each output connector can have at most one incoming wire
- Component outputs can fan out to multiple destinations
- Wires automatically validate type compatibility
Wire Validation
Type compatibility is checked as you draw the wire and again at save time. If a connection is invalid, the wire paints red with a tooltip explaining why. For example, "string output cannot connect to numeric input." Invalid wires are not allowed to save: the offending wire is highlighted directly on the canvas instead of failing with a generic error.
The same contract runs in the component engine at runtime. If a wire ever produces a type mismatch (for example, an upstream component changes type after a library upgrade), the affected instance enters an ERROR state with the offending pin called out in the diagnostics.
Allowed coercions:
int,float, andboolconnect to each other in any direction.- A value is adjusted so it matches the pin it lands on. A
floatarriving at anintpin is rounded to the nearest whole number, with.5rounding up:2.5becomes3,-2.5becomes-2. A number arriving at aboolpin becomestruewhen it is non-zero andfalsewhen it is zero. - If you need different rounding — always down, always toward zero — put a math component in front of the wire.
Identical types always pass. str, list, and dict require an exact type match.
Custom Wire Routing
Click any wire to reveal a midpoint handle. Drag it perpendicular to the wire's direction to de-stack overlapping wires or reshape the route for readability. Double-click the wire to reset it to the default path. Routing is saved with the canvas and survives copy/paste.
Dependency Ordering
When components are wired together, the engine automatically determines the correct execution order using topological sorting. Components whose outputs feed into other components' inputs execute first, ensuring data flows correctly in a single scan cycle.
If a circular dependency is detected (A feeds B, B feeds C, C feeds A), the engine logs a warning and executes the components in a best-effort order. Components in the cycle will see values from the previous scan cycle rather than the current one.
Per-Instance Pin Layouts
Every instance has its own pin layout. The component type ships a default ordering, and you can layer your own customizations on top: reorder, insert gaps for visual grouping, or hide pins you don't need on this particular instance.
Open the instance's Canvas tab and click Customize Layout... to open the editor:
- Drag pins in the input or output column to reorder them
- Insert a gap between pins for visual separation
- Hide a pin by dragging it to the hidden tray on the right
- Restore a hidden pin by dragging it back from the tray
- Reset to default to discard the instance's overrides and fall back to the component type's layout
Layouts persist across canvas saves, environment clones, and copy/paste operations. Cross-side drags (e.g., trying to move an input pin to the output column) are rejected with visual feedback.
Offline Values
Each input field can have an offline value: a fallback used when the input is not wired or when live data is unavailable. Edit offline values in the Input Fields table on the instance's Overview tab.
You can also click Save Live to Offline to capture the current runtime values as the new offline defaults. This is useful for initializing components with known-good values after a period of live operation.
Saving the Canvas
Changes to the canvas (adding/removing instances, moving nodes, creating wires, editing pin layouts, customizing wire routes) are held locally until you click Save in the toolbar. The save operation is atomic: all changes are applied together or not at all.
If a save is rejected, the canvas highlights the specific instance, connector, or wire that failed validation rather than showing a generic error.
The toolbar also provides:
Deleting happens from the selected item itself: the instance menu or connector header in the inspector, or the Delete key. On small screens the toolbar actions live in the environment menu at the right end of the bar.
Saving While the Environment Runs
You do not have to stop an environment to change it. If it is running when you save, Koios first shows a Save to running environment confirmation summarizing what is about to change — components, connectors, and wires added, modified, and removed — so you can check the scope before committing.
Confirmed changes are applied in place within one scan cycle. Components are not restarted, so timers, buffers, and other internal state carry through the change.
Monitoring Live Values
When the environment is enabled, the canvas shows live values flowing through wires and pins. Input and output fields on each instance display their current values, updating in real time at the environment's scan rate.
Open the environment settings from the gear on the environment bar and use its Execution tab for performance metrics. For per-instance log output, open the log pane from the terminal icon on the bar.
What's Next
- Component Environments: configure scan rates and monitor execution performance
- Component Libraries: manage the component types available on the canvas
- Building Components: create your own component types
