====== EVE-OS / ZEDEDA Hardware Model Creation ======
This page documents how to build a ZEDEDA Cloud **hardware model** for a new edge node, including a simpler alternative to the USB-round-trip and console/debug-shell methods.
===== The challenge =====
The two ways most people reach for to pull hardware spec info off a node running EVE-OS are:
* **USB install method** — boot the EVE-OS installer from USB; EVE-OS writes a best-effort model file back to the inventory partition of the USB stick. Requires physically getting the USB stick back after install.
* **Console + debug shell method** — get serial/KVM console access, drop into the EVE-OS debug shell, and run ''spec.sh'' manually. Requires ongoing console/KVM access to the device.
Both are fine **if you have that physical/console access**. The real problem shows up when you don't: a remote site, a device with no console port wired up, no one on hand to swap the USB stick back out, etc. In that case you're stuck — you can't SSH in to grab the info remotely, because SSH normally isn't available until the node is onboarded, and it can't be onboarded without knowing its hardware model.
There's actually a third, less well-known official option that solves exactly this: an EVE-OS installer image with SSH bootstrapped in. It still needs one physical flash-and-boot, but after that everything else — pulling ''spec.sh'', ''lspci'', etc. — happens over the network.
===== The simpler way: SSH-enabled EVE-OS image =====
You don't actually need onboarding to get SSH. EVE-OS supports a **bootstrap SSH config** baked into the installer image itself, via files placed on the config partition before flashing. This gets you a root shell over the network the moment the node boots — no console, no USB round-trip, no onboarding.
==== Prerequisites ====
* Docker running on your workstation.
* An SSH keypair (''ssh-keygen -t ed25519'' if you don't have one).
==== Steps ====
- Create the config staging directory:
mkdir -p /tmp/config/GlobalConfig
- Drop your public key in as ''authorized_keys'':
PUBLIC_KEY_PATH=~/.ssh/id_ed25519.pub
cat "$PUBLIC_KEY_PATH" > /tmp/config/authorized_keys
- Add the controller FQDN in a ''server'' file:
cat < /tmp/config/server
zedcloud.zededa.net
EOF
- Enable SSH via ''GlobalConfig/global.json'' (this file must exist for EVE-OS to load the bootstrap config at all):
cat < /tmp/config/GlobalConfig/global.json
{
"GlobalSettings": {
"debug.enable.ssh": {
"Key": "debug.enable.ssh",
"ItemType": 3,
"StrValue": "$(cat "$PUBLIC_KEY_PATH")"
}
}
}
EOF
''ItemType: 3'' = string value; ''debug.enable.ssh'' holds the actual public key, not a boolean.
- Build the installer image with the config folder mounted in:
docker run -v /tmp/config:/in --rm lfedge/eve:latest installer_raw > installer-ssh.raw
- Flash ''installer-ssh.raw'' to USB and boot the target device.
- SSH straight in:
ssh -i ~/.ssh/id_ed25519 root@
**Note:** this bootstrap SSH access only works **before** onboarding. Once the node onboards, these settings are ignored. If you want to keep SSH access afterward, re-enable it through ZCLI:
zcli edge-node update --config="debug.enable.ssh:$(cat ~/.ssh/id_ed25519.pub)"
===== Gathering the hardware spec over SSH =====
Once you have a shell, pull the same information the console/debug-shell method would give you, without needing physical access at all.
==== Get the best-effort model JSON ====
spec.sh
This produces a JSON blob (CPU count, memory, storage, and an ''ioMemberList'' of guessed adapters — VGA, USB, COM ports, NVMe, Ethernet, etc.). Save it, e.g. as ''MyModel.json''. Treat it as a draft — EVE-OS's guesses are "best effort" and usually need correcting.
==== Validate/correct each adapter ====
* **Remove logo placeholders** — ''logo_back''/''logo_front'' default to placeholder paths; blank them out or you'll get a cosmetic error.
* **PCI devices** (VGA, USB, Ethernet, etc.):
lspci -k
* **IOMMU groupings** (which devices share a passthrough group — matters for ''assigngrp''):
find /sys/kernel/iommu_groups/ -type l
* **Serial/COM ports**:
dmesg | grep -i ttyS
* **USB topology** (to confirm which physical port maps to which controller):
lsusb
lsusb -t
Cross-check each entry in the ''ioMemberList'' against this output:
* Fix or fill in empty ''PciLong'' / ''Ioports'' / ''assigngrp'' values.
* Delete entries for ports that don't actually exist as separate controllers (e.g., a device with 4 physical USB ports but only 1-2 controllers in ''lspci''/iommu output — the extra logical entries should be removed since they can't be independently passed through).
* Omit adapters you don't intend to model (e.g., NVMe, if not relevant).
Validate the final file with a JSON linter before uploading.
===== Uploading the model to ZEDEDA Cloud (ZCLI) =====
- Copy the validated JSON into the ZCLI docker container:
docker cp /path/to/MyModel.json docker_containerID:/home/zcli
- Create the hardware brand, if it doesn't already exist:
zcli brand create NAME --title=TITLE
- Create the model, linking it to the JSON:
zcli model create NAME --hardware-details=PATH/TO/JSON --brand=BRAND [--title=TITLE]
- To revise a model later, edit the JSON and:
zcli model update NAME [--hardware-details=PATH/TO/JSON] [--brand=BRAND] [--title=TITLE]
Changes propagate to every edge node currently using that model.
===== Onboarding the node =====
With the model created, onboard the edge node in ZEDEDA Cloud (GUI or ZCLI): pick the Brand/Model, choose a Hardware Identity method (Edge Node Certificate from the USB inventory partition, Onboarding Key + serial number, or a single-use installer), and finish the flow.
**Good to know:** for lab/test nodes, ZEDEDA Cloud lets you onboard with *any* placeholder model just to get connectivity up — you don't strictly need the final model in hand before the first boot. That's a second way to sidestep the chicken-and-egg problem: onboard with a generic model + Onboarding Key/soft-serial (no cert extraction needed at all), then once online, correct/replace the model later. The SSH-image method above is still preferable when you want the *real* model nailed down before onboarding at all, e.g. for image reuse across a fleet of identical devices.
===== Method comparison =====
^ Method ^ Needs console/KVM ^ Needs USB round-trip ^ Needs onboarding first ^ Notes ^
| USB install dump | No | Yes | No | Best-effort model written back to USB inventory partition |
| Console + spec.sh | Yes | No | No | Manual, slow, needs physical/remote KVM access |
| **SSH-enabled image** | **No** | **No** (still need to flash once) | **No** | Recommended — full shell access pre-onboarding |
| Onboard w/ generic model | No | No | N/A (this *is* onboarding) | Fastest to get connectivity; still need to fix model later |
===== Source references =====
* [[https://help.zededa.com/hc/en-us/articles/12109968769435-Get-an-SSH-enabled-EVE-OS-image|Get an SSH-enabled EVE-OS image]]
* [[https://help.zededa.com/hc/en-us/articles/31467631543707-Create-Hardware-Models|Compile JSON File for Hardware Model Manifests]]
* [[https://help.zededa.com/hc/en-us/articles/31973592706971-Use-the-ZEDEDA-CLI-to-Upload-a-Hardware-Model|ZCLI: Upload and Create Hardware Models]]
* [[https://help.zededa.com/hc/en-us/articles/27953925647899-Onboard-Edge-Nodes-using-Models|Onboard Edge Nodes using Models]]
* [[https://github.com/lf-edge/eve|lf-edge/eve]]
* [[https://github.com/lf-edge/eve-api|lf-edge/eve-api]]
* [[https://registry.terraform.io/providers/zededa/zedcloud/latest|Terraform Registry: zededa/zedcloud]]
* [[https://github.com/zededa/terraform-provider-zedcloud|zededa/terraform-provider-zedcloud]]