Skip to main content
Version: 2026.06

Integration 301: 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-301, function key @alice:copy, tool key alice-tool, tool display name Alice Tool.

Prerequisites

  • Access to an Istari Digital Platform environment (e.g. dev.istari.app)
  • Admin privilege to Istari, or access to an admin user to perform the required actions on your behalf
  • Docker and Docker Compose installed
  • The Istari Digital CLIdebian amd64 from the Customer Portal
  • The Istari Digital Agent installerdebian amd64 from the Customer Portal

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

Create a downloads folder in the same path as your Docker files. Download the Istari CLI and agent packages from the Customer Portal and place the files in the newly-created downloads folder.

Linux/MacOS terminal example:

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

  • Istari Digital CLI - Debian AMD64 Ubuntu 24.04 tar.gz package
  • Istari Agent - Debian AMD64 (non-FIPS) .deb package

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.gz
./stari_ubuntu_24_04-amd64/stari_ubuntu_24_04-amd64 setup

Note: You may be using a newer version of the CLI. Update the filename and folder name in the command.

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 a Personal Access Token (shown only once; store it securely) and copy the Registry URL.
  3. Go to Agents listing page → generate an Agent Token (shown only once; store it securely).

Note: To access Developer Settings, click on your name at the bottom of the left sidebar. Admin-level privilege is required to generate the agent token

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

REGISTRY_URL=<registry-url>
API_KEY=<your-personal-access-token>
AGENT_PAT=<your-agent-token>

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-301",
"module_version": "1.0.0",
"module_checksum": "placeholder",
"module_type": "function",
"module_display_name": "301 Tutorial",
"tool_key": "<namespace>-tool",
"tool_display_name": "<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-301 — unique per developer on the control plane.
  • tool_key: <namespace>-tool — a unique identifier for the tool.
  • tool_display_name: <Namespace> Tool — the human-readable name shown in the Istari Digital web app 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_display_name (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

Install the Istari Agent by using the Debian dpkg command.

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 in the web app

In the Istari Digital web app:

  • 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 Istari Digital web app:

  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.