For the complete visual behavior node, port, transition, and parameter contract, see the Device behavior flow reference.
A VDS4E device package is a self-contained, versioned directory. It is the unit
that developers copy into or remove from device-models/, publish, validate,
and configure in the server. No resource inside a package is referenced by a
repository-global path.
Create and validate packages with the public CLI:
cargo run -p vds-cli -- device-package new ./my-sensor \
--id my-sensor --name "My Sensor" --bus i2c
cargo run -p vds-cli -- device-package validate ./my-sensorThe versioned JSON contract is
schemas/device-package.schema.json,
and the Rust loader is vds_core::device_package::DevicePackage.
my-device/
├── device-package.yaml # required package manifest
├── README.md # package landing page
├── model/
│ └── device.yaml # required runtime model by convention
├── flows/
│ └── behavior.yaml # optional editable behavior document
├── scenarios/ # optional conformance scenarios
├── fixtures/ # optional binary/JSON/YAML test data
├── runtime-data/ # generated File Write outputs; not package source
├── docs/ # optional detailed documentation
└── assets/ # optional images, icons, and data files
Only device-package.yaml has a fixed filename. Other paths are declared in
the manifest, so a future package can add protocol descriptions, generated
artifacts, firmware samples, or additional flows without changing discovery.
Unknown future metadata belongs under spec.extensions, namespaced by its
owner.
When spec.authoring.behavior_flow is declared, the server compiles it during
package loading and uses it as the authoritative runtime state machine and
typed signal graph. See
ADR 0007.
File Read paths are package-relative. File Write paths must be under
runtime-data/; absolute paths, parent traversal, and symlink escapes are
rejected.
api_version: vds4e.dev/v1alpha1
kind: DevicePackage
metadata:
id: example-temperature-sensor
display_name: Example Temperature Sensor
version: 0.1.0
license: Apache-2.0
authors: [Example Labs]
tags: [i2c, sensor]
spec:
bus:
type: i2c
profile: i2c.register-device
options:
default_address: 0x48
address_bits: 7
runtime:
driver: generic-i2c-register
model: model/device.yaml
authoring:
behavior_flow: flows/behavior.yaml
validation:
scenarios: scenarios
fixtures: fixtures
documentation: docs
assets: assets
extensions:
example.com:
datasheet_revision: Ametadata.id, spec.bus.type, and spec.runtime.driver must match the
corresponding device.id, device.bus, and device.model fields in the
runtime model. All paths are package-relative. Absolute paths and ..
traversal are rejected.
The package contract can describe these bus families without changing the folder layout:
| Bus | Current executable runtime | Host exposure |
|---|---|---|
| SPI | generic-spi-command |
CUSE /dev/spidevX.Y |
| I²C | generic-i2c-register, at24c-eeprom |
CUSE /dev/i2c-N |
| GPIO | generic-gpio-bank |
kernel gpio-sim / /dev/gpiochipX |
| UART | generic-uart-responder |
PTY /dev/pts/N |
| Ethernet | not implemented | not implemented |
| CAN | not implemented | not implemented |
| USB | not implemented | not implemented |
| Custom | no generic runtime | depends on a future implementation |
The schema and package layer intentionally cover more bus families than the runtime currently executes. A package can be structurally valid while still requiring a runtime driver that has not been implemented. Server startup is the authoritative check that a configured runtime driver is executable.
- Scaffold with
vds-cli device-package new. - Implement
model/device.yamlfor the selected runtime driver. - Add an editable behavior flow and deterministic scenarios as needed.
- Run
vds-cli device-package validate <directory>. - Add the directory to
device_packagesinconfig/vds-server.yaml.
At load time, packages referenced from device-models/examples/ are copied to
~/vds4e/devices/<package-id>/. Validation, scenarios, behavior flows, assets,
and runtime models are then loaded from that installed copy rather than from
the repository tree.
Installed example copies contain a .vds4e-managed-example marker and are
refreshed on subsequent loads. An existing unmarked directory with the same
package ID is never overwritten. Set VDS4E_DEVICE_STORE to use a different
store root in CI, containers, or development environments.
6. Start the server and run package scenarios against the authoritative
runtime.
The scaffold's non-SPI models are structural starting points until their runtime drivers land; the validator deliberately validates package integrity, while server startup additionally validates the runtime model contract.
The entire directory is installed or removed as a unit. Moving it elsewhere removes its model, flow, scenarios, fixtures, docs, and assets together. Restoring it at the configured path restores the complete package after the next server load. Do not put writable simulator state in a package; runtime state belongs to the server's state/storage layer.
Package consumers must:
- resolve only manifest-declared relative paths;
- reject resources escaping the package root;
- ignore no unknown top-level fields (schema upgrades must be explicit);
- treat
api_versionas the compatibility boundary; - keep package contents read-only at runtime;
- use semantic versions for package releases.
A new bus implementation should keep transport, device semantics, and host exposure separate:
- Define the bus-specific model schema and deserialize it in the device-model layer.
- Implement the authoritative runtime device/transaction behavior.
- Dispatch
spec.runtime.driverto that implementation during registry load. - Add an adapter that exposes the bus to the host where applicable.
- Add package conformance scenarios and an end-to-end adapter test.
- Document stable
spec.bus.optionsand the model schema.
This separation permits several host adapters to attach to device instances
without embedding /dev paths or host privileges in distributable packages.