Skip to main content

Integration 101: Your First Module in 15 Minutes

Objective

Create a minimal Istari Digital Platform module using a shell script — no Python framework, no build tools. The module:

  1. Accepts an input text file
  2. Copies it to the output with a timestamp header
  3. Can be executed through the Istari Digital Platform

By completing this tutorial, you'll learn:

  • How to structure a platform module
  • How to define function inputs and outputs using the file contract
  • How to test, deploy, and run your module
  • How to set up and configure the Istari Digital Agent to execute jobs

Namespace: Throughout this tutorial, replace <namespace> with your own unique namespace (e.g. your name or team: alice, teamx). This keeps your module key unique when multiple developers share the same platform control plane.
For example: module key @alice:integration-101, function key @alice:copy, tool key alice-tool.

Prerequisites

  • Access to an Istari Digital Platform environment (e.g. dev.istari.app)
  • Docker and Docker Compose installed
  • The Istari Digital CLI for Linux (amd64) — obtain it from Artifactory.
  • The Istari Digital Agent installer for Linux (amd64) — a .deb package or tarball from the same source

Key Concepts

Modules

A module is a package containing one or more functions that the Istari Digital Agent can execute. Modules are language-agnostic but must follow a file-based contract for communication.

Your module can be anything executable: a shell script, a Python script, a compiled binary. In this tutorial we use a simple bash script.

Functions

Functions define:

  • Inputs: Models (files) and parameters
  • Outputs: Files or directories generated by the function
  • Execution: How the function runs (command, environment)

File Contract

The Agent runs your module functions by executing a command (run_command in the manifest). It passes three paths:

  • input file (input_file.json) — JSON with model file paths and parameters
  • output file (output_file.json) — your module writes a JSON array of output file metadata here
  • temp dir — Temporary directory for intermediate files

Step 1: Start the Docker Environment

1.1 Create the Dockerfile

FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y --no-install-recommends \
zip \
unzip \
ca-certificates \
jq \
nano

WORKDIR /workspace

CMD ["sleep", "infinity"]

1.2 Create docker-compose.yml

services:
istari-tutorial:
build: .
image: istari-tutorial:24.04
platform: linux/amd64
container_name: istari-tutorial
volumes:
- .:/workspace
- ./downloads:/downloads:ro
working_dir: /workspace
stdin_open: true
tty: true

Note: platform: linux/amd64 is required because the Istari Digital CLI and Agent are only available for amd64. On Apple Silicon Macs this runs under Docker emulation.

1.3 Place downloads

mkdir -p downloads
# Copy the CLI tarball and Agent .deb here, e.g.:
# cp /path/to/stari_ubuntu_24_04-amd64-v<VERSION>.tar.gz downloads/
# cp /path/to/istari-agent_<VERSION>_amd64.deb downloads/

In this tutorial we are using

  • stari_ubuntu_24_04-amd64-v0.21.1.tar
  • istari-agent_10.1.2_amd64

1.4 Start the container and open a shell

docker compose up -d --build
docker compose exec istari-tutorial bash

You are now in a bash shell inside Ubuntu 24.04 (amd64). The project root is mounted at /workspace, and downloads/ is at /downloads (read-only).

All remaining steps are run inside this container.

Editing files: The Dockerfile includes nano, so you can edit files directly inside the container (e.g. nano module_manifest.json). You can also use your preferred editor on the host — because /workspace is a mounted volume, any changes you make on the host are immediately visible inside the container.

Step 2: Install and Initialize the CLI

2.1 Install the CLI

Extract and set up the CLI from /downloads:

cd /tmp
tar -xzf /downloads/stari_ubuntu_24_04-amd64-v0.21.1.tar
./stari_ubuntu_24_04-amd64/stari_ubuntu_24_04-amd64 setup

The setup installs the Istari Digital CLI in your path; accept the prompt:

Istari Digital CLI Setup
==================================================

⚠ The 'stari' CLI is not currently on your PATH.
You're running it from: /tmp/stari_ubuntu_24_04-amd64/stari_ubuntu_24_04-amd64

Would you like to add 'stari' to your PATH now? [y/n] (y): y

Update your PATH and check stari CLI:

source ~/.bashrc
stari --version

2.2 Generate credentials and create a .env file

  1. Access the Istari Digital Platform (e.g. dev.istari.app).
  2. Go to Developer Settings → generate an API Key and copy the Registry URL.
  3. Go to Admin Settings → generate an Agent PAT (shown only once; store it securely).

Note: To access Developer Settings and Admin Settings, click on your name at the bottom of the left sidebar.

Create a .env file in your project root (on the host) with these values:

REGISTRY_URL=<registry-url>
API_KEY=<your-api-key>
AGENT_PAT=<your-agent-pat>

Inside the container, load the variables:

cd /workspace
source /workspace/.env

2.3 Initialize the CLI

stari client init "$REGISTRY_URL" "$API_KEY" --yes
stari client ping

You should see a successful connection.

Successfully connected
Successfully connected to Istari Digital at <registry_url>.

2.4 Patch the config file

Workaround: The CLI does not write a default section to the config file, but the Agent requires it. Without this step the Agent will fail with KeyError: 'default' on startup.

echo "default: {}" >> /root/.config/istari_digital/istari_digital_config.yaml

Step 3: Create the Module

No scaffolding needed. Create the module directory with just two files: a manifest and a shell script.

mkdir -p /workspace/my-module
cd /workspace/my-module

3.1 Create the shell script

Create copy.sh:

#!/usr/bin/env bash
# Istari module: copies input file to output with a timestamp header.
# Usage: copy.sh --input-file <path> --output-file <path> --temp-dir <path>
set -e

# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--input-file) INPUT_FILE="$2"; shift 2 ;;
--output-file) OUTPUT_FILE="$2"; shift 2 ;;
--temp-dir) TEMP_DIR="$2"; shift 2 ;;
*) shift ;;
esac
done

# Read the input file path from the input JSON
FILE_PATH=$(jq -r '.input_file.value' "$INPUT_FILE")

# Copy with timestamp header
OUTPUT_PATH="${TEMP_DIR}/copied_file.txt"
echo "# Processed at $(date -Iseconds)" > "$OUTPUT_PATH"
echo "" >> "$OUTPUT_PATH"
cat "$FILE_PATH" >> "$OUTPUT_PATH"

# Write the output JSON
echo '[{"name": "copied_file", "type": "file", "path": "'"${OUTPUT_PATH}"'"}]' > "$OUTPUT_FILE"

Make it executable:

chmod +x copy.sh

3.2 Create the manifest

Create module_manifest.json (replace <namespace>):

{
"module_key": "@<namespace>:agent-101",
"module_version": "1.0.0",
"module_checksum": "placeholder",
"module_type": "function",
"module_display_name": "101 Tutorial",
"tool_key": "<namespace>-tool",
"tool_versions": ["1.0.0"],
"operating_systems": ["Ubuntu 24.04"],
"agent_version": ">=9.0.0",
"internal": false,
"authors": [
{
"name": "Your Name",
"email": "you@example.com"
}
],
"functions": {
"@<namespace>:copy": [
{
"entrypoint": "copy.sh",
"run_command": "bash {entrypoint} --input-file {input_file} --output-file {output_file} --temp-dir {temp_dir}",
"operating_systems": ["Ubuntu 24.04"],
"tool_versions": ["1.0.0"],
"function_schema": {
"inputs": {
"input_file": {
"type": "user_model",
"validation_types": ["@extension:txt"],
"optional": false,
"display_name": "Input File"
}
},
"outputs": [
{
"name": "copied_file",
"type": "file",
"required": true,
"upload_as": "artifact",
"display_name": "Copied File"
}
]
}
}
]
}
}

Key points:

  • module_key: @<namespace>:agent-101 — unique per developer on the control plane.
  • tool_key: <namespace>-tool — shown in the Platform UI when granting tool access.
  • The function key @<namespace>:copy must use the same namespace as module_key.
  • run_command calls bash {entrypoint} ... — the Agent resolves {entrypoint} to the path of copy.sh.
  • The shell script parses --input-file, --output-file, --temp-dir like any CLI tool.
  • We are using a simple shell script but the same approach works with any executable, including a compiled binary.

Validate the manifest

stari module lint module_manifest.json

Step 4: Test Locally

cd /workspace/my-module
mkdir -p test_run
echo "Hello, Istari!" > test_run/input.txt

Create test_run/input.json:

{
"input_file": {
"type": "user_model",
"value": "test_run/input.txt"
}
}

Run the script:

bash copy.sh \
--input-file test_run/input.json \
--output-file test_run/output.json \
--temp-dir test_run

Check the results:

cat test_run/output.json
cat test_run/copied_file.txt

You should see the copied file with a timestamp header.

Step 5: Package and Publish

5.1 Package

cd /workspace/my-module
zip -r /workspace/my-module.zip . -x "test_run/*"

5.2 Publish to the Platform Registry

stari client publish module_manifest.json

5.3 Provide access

The user who publishes the module has access by default. To give access to other users:

  1. Open Admin Settings (click your name at the bottom of the left nav bar).
  2. Select User → select a user → open the ellipsis menu (⋯).
  3. Select Manage Tool Access.
  4. Check the tool that matches your tool_key (e.g. alice-tool).

Step 6: Set Up the Agent

The Agent runs on the machine (or container) and polls the Platform for jobs. When a job is available, the Agent executes your module locally. Both the Agent and the module must be installed on the same machine.

6.1 Install the Agent

dpkg -i /downloads/istari-agent_10.1.2_amd64.deb

The Agent is installed to /opt/local/istari_agent/.

6.2 Initialize the Agent

Use the variables from your .env file (loaded in Step 2.2):

source /workspace/.env
stari agent init "$REGISTRY_URL" "$AGENT_PAT"

Enable headless mode: Open the config file and add istari_digital_agent_headless_mode: true under the agent: section. This is required when running the Agent without a graphical display (e.g. in Docker or over SSH).

echo "    istari_digital_agent_headless_mode: true" >> /root/.config/istari_digital/istari_digital_config.yaml

6.3 Install the Module on the Agent

mkdir -p /opt/local/istari_agent/istari_modules/my-module
cp /workspace/my-module.zip /opt/local/istari_agent/istari_modules/my-module/
unzip -o /opt/local/istari_agent/istari_modules/my-module/my-module.zip -d /opt/local/istari_agent/istari_modules/my-module

6.4 Start the Agent

/opt/local/istari_agent/istari_agent_<VERSION>

Your Agent version may vary.

Expected result (successful start): You should see Agent logs with basic information:


- MainThread - INFO - Agent initialized
- MainThread - INFO - Istari agent version 10.1.2
- MainThread - INFO - Configuration: istari_digital_agent_poll_interval=15
...
- MainThread - INFO - Agent ID: 063d23f7-49aa-4386-9f58-260d2ed7cb58
- registration_thread - INFO - Got display name 'trusty-oin-4579' from server
- MainThread - INFO - No compatible jobs
- MainThread - INFO - Checking for available jobs

The Agent is polling; leave it running.

Step 7: Run the Function via the Platform

In the Platform UI:

  • Upload a .txt file.
  • Select the file and choose Create Job.
  • In the Select a tool/function dropdown, select the tool and function you created.
  • Execute the job.

This starts a Job that you can monitor in the Jobs section.

Check the Agent logs in the terminal where the Agent is running.

You should see entries like:

Found available job 6b1d8333-d0c8-4d9b-992e-975724a5e8c6
CLAIMING JOB STATE: job 6b1d8333...
EXECUTING JOB STATE: job 6b1d8333...
Running process 6b1d8333... in cwd /opt/local/istari_agent/istari_modules/my-module with command: ['bash', '/opt/local/istari_agent/istari_modules/my-module/copy.sh', '--input-file', '...input.json', '--output-file', '...output.json', '--temp-dir', '...out']
Process 6b1d8333... completed succesfully
Return code for job 6b1d8333...: 0
Job 6b1d8333... completed successfully
EXECUTION SUCCESS STATE: job 6b1d8333...

Check results

In the Platform UI:

  1. Go to the Files tab.
  2. Find your file and view the artifact generated by the job.

Cleaning Up

When you are done, exit the container and stop it:

exit
docker compose down

Summary: What You've Achieved

By completing this tutorial, you've built and deployed your first Platform module with a @<namespace>:copy function that:

  • Accepts a text file as input,
  • Adds a timestamp header to the content,
  • Returns the processed file as an artifact,
  • Runs as a job on the Istari Digital Platform.

Key Skills You've Learned

  • Module Structure: How to create a module with just a script and a manifest.
  • Function Design: Defining inputs, outputs, and execution logic.
  • Local Testing: Validating your function before deployment.
  • Deployment Workflow: Packaging, publishing, and granting access to your module.
  • Agent Integration: Setting up and running the Agent to execute jobs.

Troubleshooting

DisplayNameError: Bad display name ""

The Agent tries to start a system-tray icon, which requires a graphical display. In Docker or over SSH (headless), there is no display and the Agent crashes.

Fix: Enable headless mode in your Agent config. Add the following under agent: in istari_digital_config.yaml:

agent:
istari_digital_agent_headless_mode: true

KeyError: 'default'

The CLI doesn't write a default section but the Agent expects it.

Fix: echo "default: {}" >> /root/.config/istari_digital/istari_digital_config.yaml

Module version 1.0.0 already exists

HTTP response body: {"detail":"Malformed Manifest: \"Module version 1.0.0 already exists.\""}

The registry rejects a publish if that exact version has already been uploaded. Module versions are immutable once published.

Fix: Bump module_version in module_manifest.json (e.g. 1.0.01.0.1) and publish again.