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:
- Accepts an input text file
- Copies it to the output with a timestamp header
- 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 keyalice-tool, tool display nameAlice 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 CLI —
debian amd64from the Customer Portal - The Istari Digital Agent installer —
debian amd64from 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/amd64is 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.gzpackage - Istari Agent - Debian AMD64 (non-FIPS)
.debpackage
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/workspaceis 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
- Access the Istari Digital Platform (e.g.
dev.istari.app). - Go to Developer Settings → generate a Personal Access Token (shown only once; store it securely) and copy the Registry URL.
- 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
defaultsection to the config file, but the Agent requires it. Without this step the Agent will fail withKeyError: '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>:copymust use the same namespace asmodule_key. run_commandcallsbash {entrypoint} ...— the Agent resolves{entrypoint}to the path ofcopy.sh.- The shell script parses
--input-file,--output-file,--temp-dirlike 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:
- Open Admin Settings (click your name at the bottom of the left nav bar).
- Select User → select a user → open the ellipsis menu (⋯).
- Select Manage Tool Access.
- 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
.txtfile. - 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:
- Go to the Files tab.
- 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.0 → 1.0.1) and publish again.