Tools and Editors
AutoCore modules expose their configuration as a JSON Schema, and the IDE
renders that as a form. Some modules need more than a form — the labelit
vision module, for example, needs an image viewer with edge-detection overlays,
draggable scan regions, and live placement preview. Those richer editors, and
standalone utilities in general, are delivered as tools.
A tool is a standalone program — usually a small web server — that the platform knows about through the tool registry. The registry lets:
autocore-serversupervise long-running tools (start them with the server, stop them on shutdown, restart them on demand);autocore-idediscover which module domains have a rich editor and offer an Open editor action for them;- packages register and unregister tools automatically on install/remove.
The first tool is labelit-studio, the configuration editor for the vision (labelit) module. This chapter explains the registry, how to operate and configure tools, and how to add new ones.
Tools versus modules
| Module | Tool | |
|---|---|---|
| Connects to the server over IPC | Yes | No |
| Receives its config on the command line | Yes (--config) | No |
| Participates in the control loop | Yes | No |
| Typically a… | hardware/service driver | web UI / editor / utility |
| Discovered from | project.json modules | the tool registry (tools.d/) |
| Examples | modbus, ethercat, ni, labelit | labelit-studio |
A tool is independent of any one project — its editor is available even when no
project is loaded. The vision module (labelit) runs the camera and label
placement; the vision tool (labelit-studio) edits that module’s config.
The registry on disk
Everything lives under the AutoCore config directory (/srv/autocore/config on
a deployed machine; AUTOCORE_CONFIG_DIR overrides it):
/srv/autocore/config/
├── config.ini # server + module config
├── tools.d/ # MANIFESTS — owned by the installing package
│ └── labelit-studio.json
└── tool-settings/ # SETTINGS — runtime, survive upgrades
└── labelit-studio.json
Two ideas matter:
- The manifest is package-owned; the settings are not. A manifest
describes what a tool is (its executable, what it edits, defaults) and is
shipped inside the
.deb, so an upgrade replaces it freely. Settings are what an admin chose (the actual port, enabled/disabled, bind address) and live in a separate directory the package never touches, so upgrades never reset a field-configured port. - dpkg owns the manifest file. A package contributes a tool simply by
shipping its manifest into
tools.d/. Installing the package registers the tool; removing it deregisters — handled by the package manager, with no shared file to edit.
So there are three distinct kinds of config; don’t confuse them:
| Scope | Lives in | Who writes it |
|---|---|---|
| Module operational config | project.json → modules.<name>.config | the editor / the IDE |
| Tool manifest | tools.d/<tool>.json | the installing package |
| Tool settings | tool-settings/<tool>.json | admin / the tool |
Manifest format
{
"schema_version": 1,
"name": "labelit-studio",
"version": "1.1.3",
"description": "Vision-pipeline configuration editor for autocore-labelit",
"executable": "/opt/autocore/bin/modules/labelit-studio",
"serves_http": true,
"ui_path": "/",
"launch": {
"mode": "service",
"default_port": 7878,
"autostart": true,
"args": []
},
"editors": [
{ "target_domain": "labelit", "target_path": null, "label": "Vision Editor" }
]
}
| Field | Meaning |
|---|---|
name | Unique tool id (kebab-case); must match the file stem. |
executable | Absolute path to the binary. |
serves_http / ui_path | Whether it serves a web UI, and the root path. Drives the security gate and lets a browser/IDE point at it. |
launch.mode | service (long-running; the server supervises it) or on_demand (invoked by the IDE/CLI; never auto-started). |
launch.default_port | Default HTTP port; the effective port comes from settings. |
launch.autostart | Default for starting with the server; effective value comes from settings. |
launch.args | Extra arguments inserted before the standard --port/--bind. |
editors[] | The config editors the tool contributes. Each binds to a module target_domain and an optional target_path (a JSON pointer into that module’s config). null path = the whole module config. One tool may contribute several. |
Settings format
{
"launch": { // the SERVER reads exactly these fields
"enabled": true,
"port": 7878,
"bind": "127.0.0.1",
"autostart": true
},
"extra": { // free-form, tool-private; the server never parses it
"last_image_dir": "/home/me/snaps"
}
}
The file is created on first run, seeded from the manifest’s launch defaults.
After that, on-disk values win — so an admin’s port choice survives upgrades.
Security gate. A
serves_httptool is an unauthenticated surface (it can edit config and, on a target, trigger the camera). Until AutoCore adds user authentication,binddefaults to127.0.0.1(loopback only). Binding a tool to a routable address is a deliberate admin choice — prefer reaching it over Tailscale rather than exposing it on the LAN.
How the server runs tools
On startup the server scans tools.d/. For every manifest with
launch.mode = "service" whose settings have it enabled with autostart, it
launches:
<executable> [launch.args...] --port <settings.port> --bind <settings.bind>
and stops it on shutdown — the same lifecycle as a module, but discovered from
the registry instead of project.json. on_demand tools are not auto-started.
Two server commands manage this at runtime (also reachable with acctl, below):
| Command | Effect |
|---|---|
system.list_tools | List registered tools with live running state and URL. Used by the IDE to discover editors. |
system.rescan_tools | Re-read the registry: start newly installed service tools, stop removed/disabled ones, leave the rest running. |
acctl tools
acctl tools list # what the server discovered, with running state + URL
acctl tools rescan # reconcile running tools with the registry, no restart
acctl tools list prints each tool, whether it is running, its URL, and the
module domains it edits. acctl tools rescan is what package scripts call so an
install or uninstall takes effect on a running server without a restart.
Registering a tool from a package
A .deb registers a tool with two pieces:
-
Ship the manifest as a packaged file into
tools.d/. InCargo.toml(cargo-deb):[package.metadata.deb] assets = [ ["target/release/labelit-studio", "/opt/autocore/bin/modules/", "755"], ["packaging/tools.d/labelit-studio.json", "/srv/autocore/config/tools.d/labelit-studio.json", "644"], ]Because dpkg owns the file, install registers and remove deregisters — no maintainer-script logic needed for the registration itself.
-
Poke a running server so the change is live, from
postinst/postrm:poke_rescan() { for ACCTL in acctl /opt/autocore/bin/acctl; do if command -v "$ACCTL" >/dev/null 2>&1; then "$ACCTL" tools rescan >/dev/null 2>&1 || true return 0 fi done }This is best-effort: if the server is down or
acctlis absent, the tool is picked up on the next server start anyway.
labelit-studio: the worked example
labelit-studio is a web-based editor for the vision pipeline — it loads
images, runs the exact production pipeline the labelit module runs, shows
each stage (edges, masks, placement) with overlays, has TM-X-style tools (Canny,
threshold, HSV mask, pixel probe), drag-to-set scan/background regions, and the
calibration wizards (checkerboard, known-size, auto-canny). What it shows for a
given config is, by construction, what the module will do with that config.
It runs in three contexts — the same binary every time:
1. Standalone (laptop / conference demo)
No server, no project. Build it (or install the .deb) and run it against local
images:
# from an autocore-labelit checkout
cargo build --release --bin labelit-studio
./target/release/labelit-studio --config project.json --images ./snaps --port 7878
# then open http://localhost:7878/
--config points at any project.json (or a module-level config export);
--images is a file or folder; you can also open files from inside the UI. Use
--bind 0.0.0.0 only if you want another device on the network to reach it.
2. On a target (server-supervised)
Install autocore-labelit on the machine. The deb ships the binary and its
manifest, and the running server starts it automatically (loopback, port 7878 by
default). An operator with only a browser opens it:
http://<target>:7878/
To change the port, enable/disable it, or bind it wider, edit
/srv/autocore/config/tool-settings/labelit-studio.json and run
acctl tools rescan (or restart the server). Remember the security gate before
changing bind.
3. In the IDE (autocore-ide)
Open your project in VS Code / VS Codium. In the autocore Project view, the
labelit module shows an $(preview) Open editor action. It spawns
labelit-studio as a local sidecar against your workspace project.json and
hosts its UI in a panel — fully offline, no server needed. The IDE remains the
sole writer of project.json (it preserves your comments and formatting). See
the autocore-ide manual for details and the autocore.labelit.studioPath /
autocore.labelit.imagesDir settings.
Building a self-contained studio
For a binary that runs on a clean machine with no apt install (the field
.deb, or bundling into the IDE .vsix), labelit-studio is built camera-free
and against a static OpenCV:
- The studio never opens a camera directly (on a target it snaps through the
module over IPC), so Aravis/GLib is behind the default-on
camerafeature and is dropped with--no-default-features. - A minimal static OpenCV removes the
libopencvruntime dependency.
# in the autocore-labelit repo
OPENCV_VERSION=4.10.0 PREFIX=/opt/opencv-static ./scripts/build-static-opencv.sh
PREFIX=/opt/opencv-static ./scripts/build-studio-selfcontained.sh
# -> target/release/labelit-studio, ldd shows only glibc / ld-linux
The full recipe (and how to wire the result into the deb and the vsix) is in the
autocore-labelit repo’s doc/static-build.md.
Adding a new editor
To give another module (say an EtherCAT slave-tree editor) a rich editor:
- Build the tool as a standalone HTTP server that accepts
--portand--bind. (It can be anything — the registry only cares about the manifest.) - Write a manifest declaring
serves_http: true,launch.mode: "service", and aneditorsentry whosetarget_domainis the module domain it edits. - Ship it in the tool’s package: the binary plus the manifest as a
dpkg-owned file in
tools.d/, and apostinst/postrmrescan poke. - (Optional) Teach the IDE about it offline by adding it to the IDE’s bundled fallback list, so the Open editor action appears without a live server connection.
The server will discover and supervise it; the IDE will offer it for that
module domain; acctl tools list will show it. No changes to autocore-server
or autocore-ide core are required to add a tool.