> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ilyama.golain.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Ship a node

> Upload a node as an OTA release artifact, register it with a place_file step, and confirm it in the Console's Node Registry.

A node installs through the ordinary OTA `place_file` step. There is no separate node install path. The one node-specific addition is the **Register in node registry** flag, which tells Omega to introspect the placed library and add it to the device's node catalog.

## Before you upload

* Verify the library on its target platform with `omega-agent __node-introspect` as described in [Build a node](/edge/nodes/build). Registration runs the same check and refuses the same failures.
* Give the file a plain name with no path, for example `libhello_node.so`. Ship one artifact per platform and architecture.
* Keep one domain per node. Two nodes claiming the same domain replace each other in the registry.

## Upload as a release artifact

In the Console open **OTA** → **Releases**, create a release, and add the library as an artifact with a version. The platform records the artifact's SHA-256 and signs it with its role key. Omega verifies that signature against the trust-set on the device before the file is written, so an artifact that was tampered with in transit is never placed and never loaded.

Releases and deployments are covered in [OTA updates](/console/ota).

## Add the `place_file` step

In the release's plan, add a **`place_file`** step for the artifact and check **Register in node registry**. Set **Node version** to the version you want the registry to show. Leave **Destination** blank.

The same plan as YAML:

```yaml theme={null}
plan_version: 1
components:
  - name: hello-node
    steps:
      install:
        - step_type: place_file@1
          artifact: libhello_node.so@1.0.0
          params:
            register_node: true
            node_version: 1.0.0
```

> [!WARNING]
>
> A `register_node` step must not set `dest_dir`. A registered node has exactly one home, `dll/{domain}/{file_name}` under the Omega root, and the domain is only known after the device introspects the library. A step that sets both is rejected at plan validation with `place_file_dest_dir_not_allowed_for_register_node`.

| Omega root | Default |
| - | - |
| Linux | `/var/lib/omega` |
| macOS | `/Library/Application Support/omega` |
| Windows | `C:\ProgramData\omega` |

So the hello node on a Linux device lands at `/var/lib/omega/dll/hello/libhello_node.so`.

## What the device does

When the deployment reaches a device, Omega:

1. Downloads the artifact and verifies its role-bound signature. An unsigned or tampered file is never written.
2. Lands the file in a staging directory and runs the protected-path checks.
3. Introspects it in an ephemeral child process: ABI version, domain, capabilities, health. On Windows it runs `regsvr32 /s` first and discovers the node's CLSID itself.
4. Moves the file to `dll/{domain}/{file_name}`.
5. Writes the node to the local registry, unless health is `unavailable`, the ABI is unsupported, the domain is empty, or an op is named `unregister`.
6. Publishes the full node catalog on `node/report/registry`.

Updating a node is the same step with a new artifact version. Omega re-introspects and replaces the registry row.

## Confirm in the Console

Open **OTA** → **Node Registry**. The device row lists the domain, version, ABI, health, whether the node is enabled, and each op with a **Run** button.

Click **Run** on `greet`. The result appears against the device with the node's return code and message:

```
code=0  Hello, omega!
```

`ping` has a cadence, so it also appears in the schedule the device reports, and it runs every hour without anybody clicking.

On the device itself:

```bash theme={null}
omega-agent __node-health --domain hello
```

prints the node's current health as Omega sees it.

## Disable or remove a node

**Disable** in the Node Registry keeps the file and the registration but makes every invoke return `-102` until it is enabled again.

Every node also has a synthesised `unregister` op that Omega implements on the node's behalf. Running it drops the registry row, unregisters the COM class on Windows, and deletes the file. Your node never implements `unregister` and must not declare an op by that name.

## Troubleshooting

| Symptom | Cause | Fix |
| - | - | - |
| Step fails with `reported unavailable at load` | `node_health` returned `unavailable` | Return `degraded` for a partially working node. `unavailable` is a refusal. |
| Step fails with `node ABI N unsupported` | `node_abi_version` is not `1` | Build against the `node-abi` revision that matches the deployed Omega. |
| Step fails with `declares op 'unregister'` | Reserved op name | Rename the op. |
| Step fails with `dest_dir_not_allowed_for_register_node` | Step sets a destination | Clear **Destination**. |
| Invoke returns `-105` | Signature verification failed at load | The file on disk no longer matches the signed artifact. Treat as a security signal, then redeploy. |
| Invoke returns `-104` | The op ran longer than 60 seconds | Do long work in the background and return a status; the message is a status line, not a transcript. |
| Invoke returns `-101` | No node registered for the domain | The install has not landed yet. The platform waits and retries. |
| Linux load fails with `GLIBC_2.xx not found` | Built against a newer glibc than the device has | See the cross-compile note in [Build a node](/edge/nodes/build#cross-compile-a-linux-so-from-a-mac). |
| Registration succeeded but ops never appear | The device has not published `node/report/registry` | Check the device is connected; the catalog is republished on reconnect. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.