Advanced module patterns

Some use cases require approaches beyond the standard module workflow. This page covers three advanced patterns: defining a new resource API, deploying custom components as remote parts, and packaging modules with Docker.

Define a new resource API

You can define a new, custom resource API if:

  • You have a resource that does not fit into any of the existing component or service APIs.
  • You have a resource that could fit into an existing API, but you want different methods and messages.

Steps to define a new API

Viam uses protocol buffers for API definition. To define a new API:

  1. Decide whether your custom API is a component (interfaces with hardware) or a service (provides higher-level functionality).

  2. Choose a name for your API (called the subtype). Determine a valid API namespace triplet. For example, your-org-namespace:component:gizmo.

  3. Create a directory for your module with a src subdirectory.

  4. Write the proto methods in a <API name>.proto file inside src/proto/. For reference:

  5. Define the proto methods in Python or Go in a file called api.py or api.go:

  6. Generate the required configuration files (buf.yaml, buf.gen.yaml, buf.lock). See the Buf documentation.

  7. Use the protobuf compiler to generate all other necessary protocol buffer code from your .proto file.

After defining your API

Once your API is defined, create a model that implements it.

Keep in mind:

  • You cannot use SDKs to call your new API unless you build out the client to support it. Write code against the API in the language you used to define it.
  • You need a local copy of the module code on whatever machine runs client code against it.
  • Import the API definition from the module directory. For example, in Python: from path.to.module.src.gizmo import Gizmo.

Custom components as remote parts

Running modular resources on the computer directly connected to your components is the preferred approach. However, if you cannot use modular resources because you need to host viam-server on a non-Linux system or have a compilation issue, you can code a custom resource implementation, host it on a server, and add it as a remote part of your machine.

Once configured, you control the custom component with the Viam SDKs like any other component.

Steps

  1. Code a new model of a built-in resource type by creating a new interface that implements the required methods from its API definition.
  2. Register the custom component on a new gRPC server instance and start the server.
  3. Add the server as a remote part of your machine.
  4. (Optional) Ensure the remote server automatically starts when the machine boots.

Each remote server can host one or many custom components.

  1. Code a new model of a built-in resource type by subclassing it (for example, sensor or arm). Implement any required methods from its API definition.
  2. Register the custom component on a new gRPC server instance using the viam.rpc library.
  3. Add the server as a remote part of your machine.
  4. (Optional) Ensure the remote server automatically starts when the machine boots.

Each remote server can host one or many custom components.

Deploy a module using Docker

In rare cases, you may need to package and deploy a module using Docker. Use cases include:

  • Your module has complex system dependencies that cannot be easily installed on a machine.
  • You use a large container image and some layers are already cached on the machine.
  • You have specific security requirements.

If you deploy using Docker, create a “first run” script to handle setup. This is not recommended for modules that do not use Docker.

Use a first_run script

  1. Create a tarball containing:

    • The module’s entrypoint script or binary:

      #!/bin/bash
      exec docker run <YOUR_CONTAINER_IMAGE> <YOUR_CONTAINER_OPTIONS>
      
    • A meta.json file.

    • A first-run script that runs during setup:

      #!/usr/bin/env bash
      docker pull mongo:6
      echo "Setup complete."
      exit 0
      

    This example Makefile builds a module binary and bundles it with a meta.json and first-run script.

  2. Edit meta.json to include the first_run field:

    {
      "first_run": "first_run.sh"
    }
    
  3. Configure the module on your machine normally. The first-run script executes once when viam-server receives a new configuration, and once per version update.

  4. (Optional) To force the first-run script to run again without changing the module version, delete the marker file at .viam/packages/data/module/<id>/bin.first_run_succeeded.

  5. (Optional) The default first-run timeout is 1 hour. Adjust with "first_run_timeout": "5m" in the module configuration.