Skip to content

Your first plugin

This tutorial creates a small native plugin in its own directory. Install the cargo-dokime extension through Cargo from the Git revision published with your release. Rust supplies rustc and cargo; Cargo discovers the extension when you type cargo dokime. The desktop installer installs the GUI and the dokime CLI side by side and puts both on your PATH; cargo-dokime is a separate Cargo install.

Terminal window
cargo install --git https://gitlab.com/stokker-technologies/open-source/dokime.git --rev <release-revision> --locked cargo-dokime

Use the full source revision recorded by the release in place of <release-revision>.

1. Check your development tools and source access

Section titled “1. Check your development tools and source access”

Install Rust with rustup, Git, and the native build tools. On Windows, use Rust’s MSVC toolchain with Visual Studio Desktop development with C++ build tools and the Windows SDK. Restart your terminal or editor after installation so it sees the updated PATH.

Terminal window
rustc --version
cargo --version
git --version
cargo dokime --version

The starter depends on the Dokime SDK from Git, pinned to the full revision recorded in crates/sdk-pin.rs and embedded in your CLI. This committed pin is independent of local commits, forks, and environment variables. It also fetches Dokime’s pinned Harmos dependency. Your GitLab account and local Git authentication must allow access to both repositories:

Terminal window
git ls-remote https://gitlab.com/stokker-technologies/open-source/dokime HEAD
git ls-remote https://gitlab.com/stokker-technologies/open-source/harmos HEAD

If either command reports an authentication or access failure, configure your Git credential helper and obtain repository access before building. A successful application installation does not grant GitLab source access. The release’s SDK revision must remain available from its Git source for a fresh build to work. Keep the generated Git dependency when starting from this release.

If Git authentication works but Cargo cannot fetch the same repository, Cargo can use your Git CLI credential setup. Add this to .cargo/config.toml in the plugin project, merging it with any existing configuration:

[net]
git-fetch-with-cli = true

From an existing parent directory:

Terminal window
cargo dokime new my-plugin
cd my-plugin

new generates from templates embedded in the installed CLI and does not need network access. The first Cargo build fetches the SDK and its dependencies. The directory name becomes the package name; use --name NAME to choose another name. The command refuses to overwrite an existing destination.

The project is one native plugin crate holding a small thermal bench. src/lib.rs names the catalogs of types the plugin contributes and implements dokime::Plugin — its configuration, error and resource types, its settings, and the handles it opens once; src/main.rs starts the native process; src/error.rs holds the one error type every fallible signature answers with — a domain variant per thing the bench can discover, plus Sdk(#[from] dokime::Error) wrapping every SDK boundary the template reaches (a setting, a secret, a prompt, a captured record, an evidence file) once, instead of one variant per boundary; src/bench.rs is the simulated sensor and heater, which is the one file that pretends and the first to replace with a real driver; and src/suites/soak/hold.rs is the procedure — its parameters, the handles one attempt owns, the traceability it states, its steps, its own sampler, its two criteria and its cleanup. The layout is vertical for the rest: src/samplers/, src/listeners/ and src/routes/ each hold a mod.rs with nothing but the catalog enum, and one file per contribution — a sampler beside the row type it measures, a listener, a route. The generated README explains the rest. Keep the SDK Git revision in Cargo.toml and commit the generated Cargo.lock after resolving dependencies so teammates build from the same sources.

Two curated preludes are the canonical opening of those two kinds of file. src/lib.rs, and every file under src/samplers/, src/listeners/ and src/routes/, open with the plugin’s own:

use dokime::plugin::prelude::*;

src/suites/soak/hold.rs, and every template file, open with the one a template writes against:

use dokime::prelude::*;

Each glob names everything that kind of file typically needs — the lifecycle trait and its contribution traits on the plugin side, the six stage grants and the document types on the template side — so one import replaces the handful of dokime::execution::…, dokime::document::… and dokime::plugin::… paths an earlier SDK required. Keep a named import beside it for anything a file uses outside that set; neither prelude carries Error, Result, Value, Parameter, a builder type, or a raw map, because a plugin declares its own Error and Result and a glob that shadowed either is the one collision that bites.

The embedded starter is the primary starting point and uses every part of the authoring surface. The shipped Synthetic example is a fuller application and device bench; use it when you need an embedded Harmos runtime.

Terminal window
cargo dokime dev

Before compilation, the CLI reports the selected host and the CLI, host, and SDK versions and checks their supported compatibility series. By default it finds the Dokime installed by the desktop installer automatically; pass --dokime PATH to point it at a different host explicitly. Passing this early check still leaves normal bundle validation and runtime catalog admission to the host.

The command builds a bundle and starts an isolated development backend. The GUI attaches to its native gRPC readiness endpoint and closes with the supervised backend. Use cargo dokime dev --headless for terminal-only development. To attach a separate GUI, run cargo dokime gui --endpoint http://127.0.0.1:8765 with the printed native endpoint. Select another backend port with cargo dokime dev --port 8766.

In Plan, select the Thermal Soak suite and the Hold at Temperature template. Start the cycle from Execution. Check that the steps run through to Operator Check and that Temperature stayed in band passes, and look at the Traceability card for the serial number and firmware the template read off the simulated heater. Turn the Ask the operator to sign off parameter on to see the checkpoint prompt. This confirms that the generated catalog was admitted, the native plugin executed, and its evidence reached the desktop GUI.

The Telemetry view carries more than the soak’s own sampler. The template attribute in src/suites/soak/hold.rs names samples(host::System), which asks the host to lease the machine channel — cpu, memory and battery — for every execution of this template, so its rows are captured as that run’s evidence beside the ones the soak measures itself. Name the other host channels there in the same way when a bench correlates the machine’s radios against the unit: host::Network, host::Wifi, host::WifiLatency, host::WifiChannelOccupancy and host::Bluetooth. Nothing in that list states a cadence, because the cadence is not the requester’s to choose: each channel’s sampler declares how often it measures, and every execution leasing it shares the one running instance and its tick. The Traceability card also carries a derived Leases section naming which channels the attempt held and for how long.

The development host uses temporary runtime data and loads only the plugin bundle under development. Add --state-dir PATH if you want to retain development history between sessions. For an existing Cargo workspace, select the member from its root:

Terminal window
cargo dokime dev -p my-plugin

If your plugin declares a settings catalog secret with an environment default, the development host resolves it the same way the installed CLI does: an already-exported variable always wins, otherwise it searches upward from its working directory for a .env file — the same directory cargo dokime dev was invoked from, unless you pass --cwd DIR. This walk-up can silently pick up an unrelated .env from a parent directory (for example, a monorepo root) with a stale value for the same variable name, which reaches your plugin without any error — the classic way a variable you are sure you “set” still shows up wrong.

Use cargo dokime dev --env-file PATH to pin exactly one file instead of searching; a missing file is refused rather than silently skipped. The dev banner states where the environment comes from — the explicit file, the .env the walk-up found, or that none was found — and lists the name of every environment secret your plugin’s settings declare, with whether each is present and non-empty in the environment the host will get. Names only: values are never printed. The host reads its environment once, at startup, so editing the file or re-exporting a variable requires restarting dev (type q and Enter, then run it again) — a hot reload alone does not pick up the change.

Keep dev running. In src/suites/soak/hold.rs, change the Hold step label, save, and watch the terminal. Dev rebuilds while the current host continues serving. Once the build succeeds, it cancels this plugin’s active executions, waits for their evidence and resources to settle, and replaces only the plugin. The host, browser connection, selected case, and entered inputs stay available. Use Re-run to execute the new build; previous attempts keep their original snapshots and evidence.

Add --rerun to automatically re-run the most recently executed case from this plugin after each successful reload. First select and run a case in the browser. Each automatic re-run adds an attempt with the retained case inputs and the new plugin catalog. Automatic execution is off by default.

An initial compilation or metadata failure leaves dev watching for your fix. If a rebuild fails after a host has started, that host remains available. Fix the error and save again; the next successful build can replace it. An invalid bundle leaves the old plugin running. If replacement initialization fails after the old plugin stops, the host stays available and dev waits for a fix. Type r and Enter to retry without editing a file, or q and Enter to stop.

The terminal shows build, cancellation, reload, and readiness phases with full iteration timing. A failed step prints its message, its cause chain, the source location, and the spans it happened inside; a panic inside a step prints the same block rather than killing the plugin process. See what a failure carries. Add --verbose for detailed runtime tracing. Cargo diagnostics appear live on stderr. Use --color never for plain logs or --color always to force color; the default is --color auto.

Cargo compilation and metadata have no default timeout, including the first build’s dependency downloads and compilation. To impose a deadline, use --cargo-timeout SECONDS, such as cargo dokime dev --cargo-timeout 1200. The limit applies separately to every Cargo invocation. Catalog export and the rustc host probe retain their own bounded deadlines.

Press Ctrl-C to stop development work and shut down the development host. The generated project also runs its whole procedure without the browser:

Terminal window
cargo test

tests/soak.rs is that test, and it is written the way the plugin is used. dokime::plugin::testing::Harness supplies what a host would have supplied and hands back a typed report of what the run produced:

use dokime::plugin::testing::Harness;
use dokime::host::ToolConnectionAcquired;
let report = Harness::template("my-plugin.soak.hold")
.parameter("hold_samples", 3)
.setting("sensor_port", "sim")
.publish(ToolConnectionAcquired {
resource_id: "sim-chamber-1".into(),
resource_kind: "chamber".into(),
connection_transport: None,
connection_endpoint: None,
status_message: None,
})
.run::<my_plugin::Plugin>(my_plugin::Config::default())
.await
.expect("the soak runs");
assert!(report.passed(), "{}", report.diagnostics());
assert_eq!(report.traceability("dut").row("firmware").text(), "sim-1.0");

.parameter, .setting and .secret are the case and the admitted values; .publish files a record the way the host files one — as the declared type itself, so the test names no id — and a step that waits for another party finds it; .answer(prompt_type, action, values) decides an operator question, and a prompt nobody answers is continued from the template’s own declaration rather than left hanging. The report exposes passed(), criteria(), samples::<C>() and records::<R>() read back through the same declarations the run published through, traceability(section), artifacts() and the diagnostics() of a failure. It tests authored execution in process; the desktop check still exercises host admission and transport. Generated starters retain the API supported by their published SDK pin.

Before troubleshooting a first build, run cargo dokime doctor (with -p NAME in a workspace). It checks tools, host compatibility, SDK dependency access, and watched paths without compiling the plugin.

Both build and dev accept --features, --all-features, --no-default-features, --locked, and --offline. Use --watch PATH for extra build inputs and --ignore PATH for generated files. Workspace Cargo configuration and toolchain files are watched automatically. An unchanged bundle does not interrupt a plugin.

Terminal window
cargo dokime build --release
cargo dokime inspect target/dokime/my-plugin-0.1.0.dokime

Use the output path printed by build if you changed the package name, version, or target directory. Build diagnostics go to stderr; stdout contains the final bundle path. inspect writes JSON describing the verified bundle without running its executable.

Stop dev, start your normal Dokime application, then install into that host through its native gRPC endpoint:

Terminal window
cargo dokime install target/dokime/my-plugin-0.1.0.dokime --endpoint http://127.0.0.1:8765

Use your application’s actual native endpoint if it differs. Review the destination, package, executable owners, and requested authorization before consenting. You can also upload the bundle through Settings → Plugins. Restart the normal application to activate the installation; installation consent does not restart it for you. Repeat the soak in that application after restart.

See plugin bundles for multiple executables, assets, installation recovery, and the complete build and activation contract.

Dokime markDokime · Stokker Technologies