Istari Integration SDK 101 : Building Your First Module
Objective
In this tutorial, you'll create a simple Istari module called tutorial-module with a @<namespace>:copy function that:
- Accepts an input text file
- Copies it to the output with a timestamp header
- Can be executed through the Istari platform
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 Istari control plane.
For example: module key@alice:integration-101, function key@alice:copy, tool keyalice-tool.
By completing this tutorial, you'll learn:
- How to structure an Istari module
- How to define function inputs and outputs
- How to test, deploy, and run your module
Prerequisites
- Access to an Istari environment (e.g.
dev.istari.app) - Docker and Docker Compose installed
- The Istari CLI for Linux (amd64) — obtain it from your usual source (e.g. Artifactory or internal distribution)
- The Istari Agent installer for Linux (amd64) — a
.debpackage or tarball from the same source
Key Concepts
Modules
A module is a package containing one or more functions that Istari can execute. Modules are language-agnostic but must follow Istari's file-based contract for communication.
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
Istari communicates with modules using:
- Input file (
input_file.json): Contains model paths and parameters - Output file (
output_file.json): Where your module writes results - Temporary directory: For intermediate files
Step 1: Start the Docker Environment
All tutorial steps run inside a Docker container (Ubuntu 24.04, amd64). Your project files are mounted into the container so everything you create is visible on your host.
1.1 Create the Dockerfile
Create a file named Dockerfile in your project root:
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 \
python3-pip \
python3-venv \
zip \
unzip \
ca-certificates \
iputils-ping \
binutils \
libpython3.12
RUN pip install --break-system-packages "pydantic>=2" poetry
WORKDIR /workspace
CMD ["sleep", "infinity"]
1.2 Create docker-compose.yml
Create a file named docker-compose.yml in your project root:
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 CLI and Agent are only available for amd64. On Apple Silicon Macs this runs under Docker emulation.
1.3 Place the Linux CLI in downloads
mkdir -p downloads
# Copy your Linux CLI tarball here, e.g.:
# cp /path/to/stari_ubuntu_24_04-amd64-v<VERSION>.tar.gz downloads/
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.
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-v<VERSION>.tar.gz
./stari_ubuntu_24_04-amd64/stari_ubuntu_24_04-amd64 setup
The CLI setup install Istari Digital CLI in your path, accept the option:
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
- Open your Istari environment (e.g.
dev.istari.app). - Go to Developer Settings → generate an API Key and copy the Registry URL.
- 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
You can also set these as environment variables directly instead of using a file.
2.3 Initialize the CLI
stari client init "$REGISTRY_URL" "$API_KEY" --yes
stari client ping
You should see a successful connection. This configuration is used for all stari commands (scaffold, lint, publish).
Successfully connected
Successfully connected to Istari Digital at https://fileservice-v2.dev2.istari.app.
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: Scaffold the Module
cd /workspace
stari module scaffold tutorial-module \
--type python-module \
--author "Your Name" \
--email "you@example.com" \
--description "Tutorial module with a file copy function" \
--version "1.0.0"
This creates:
tutorial-module/
├── module_manifest.json
├── module/
│ ├── functions/
│ └── utils/
├── tests/
├── scripts/
└── README.md
Install dependencies:
cd tutorial-module
poetry install
Remove the scaffold example functions
The scaffold creates several example function files. Remove them so we start clean:
rm module/functions/data_extraction.py
rm module/functions/model_to_artifact_no_auth.py
rm module/functions/model_to_artifacts_no_auth.py
rm module/functions/model_to_artifacts_basic_auth.py
rm module/functions/model_to_artifacts_oidc_auth.py
rm module/functions/model_params_to_artifacts_no_auth.py
Keep module/functions/__init__.py and module/functions/registry.py — they are needed.
Step 4: Define the Function in the Manifest
Replace the contents of module_manifest.json with the following. Replace <namespace> with your chosen namespace.
{
"module_key": "@<namespace>:agent-101",
"module_checksum": "1234567890",
"module_version": "1.0.0",
"module_type": "function",
"module_display_name": "Tutorial Module",
"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": "dist/python_module",
"run_command": "{entrypoint} FileCopy --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": "artefact",
"display_name": "Copied File"
}
]
}
}
]
}
}
Key points:
module_key:@<namespace>:101— unique per developer on the control plane.tool_key:<namespace>-tool— shown in the UI when granting tool access.- The function key
@<namespace>:copymust use the same namespace asmodule_key. - The
run_commanduses the form{entrypoint} FileCopy .... The agent resolves{entrypoint}to the binary path, and passesFileCopyas the function name — this must match the name used inregister()in the Python code.
Validate the Manifest
stari module lint module_manifest.json
Fix any reported issues until validation passes.
Step 5: Implement the Function
Create module/functions/file_copy.py:
import logging
from pathlib import Path
from datetime import datetime
from typing import List
from pydantic import BaseModel, ConfigDict, Field, ValidationError
from module.functions.base.function_io import Input, Output, OutputType
from module.functions.registry import register
logger = logging.getLogger(__name__)
class FileCopyInput(BaseModel):
input_file: Input[str] = Field(..., description="The text file to copy.")
model_config = ConfigDict(extra="allow")
def file_copy(input_json: str, temp_dir: str) -> List[Output]:
"""Copies input file to output with a timestamp header."""
try:
input_data = FileCopyInput.model_validate_json(input_json)
except ValidationError as e:
raise ValueError("Invalid input for file_copy.") from e
input_path = Path(input_data.input_file.value)
content = input_path.read_text(encoding="utf-8")
output_path = Path(temp_dir) / "copied_file.txt"
header = f"# Processed at {datetime.now().isoformat()}\n\n"
output_path.write_text(header + content, encoding="utf-8")
logger.info(f"Copied {input_path} to {output_path}")
return [Output(name="copied_file", type=OutputType.FILE, path=str(output_path))]
# The registered name must match the second argument in run_command
register("FileCopy", file_copy)
Important: The name
"FileCopy"inregister(...)must match the function name in the manifest'srun_command({entrypoint} FileCopy ...).
Step 6: Test Your Module Locally
Run the following from the tutorial-module directory.
6.1 Create test files
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"
}
}
6.2 Run the function
python3 -m module FileCopy \
--input-file test_run/input.json \
--output-file test_run/output.json \
--temp-dir test_run
6.3 Verify output
cat test_run/output.json
# [{"name": "copied_file", "type": "file", "path": "test_run/copied_file.txt"}]
cat test_run/copied_file.txt
# # Processed at 2026-02-25T12:00:00.000000
#
# Hello, Istari!
Step 7: Build, Package, and Publish
7.1 Build the binary
Build a self-contained executable with PyInstaller. This is what the agent runs via {entrypoint} in the manifest:
cd /workspace/tutorial-module
poetry run poe build_binary
This creates dist/python_module (matching the entrypoint in module_manifest.json).
7.2 Package the module
cd /workspace/tutorial-module
zip -r /workspace/tutorial-module.zip .
7.3 Generate checksum
sha256sum /workspace/tutorial-module.zip
Update the module_checksum field in module_manifest.json with this value.
7.4 Publish to Istari
stari client publish module_manifest.json
7.5 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_key(e.g.alice-tool).
Step 8: Set Up the Agent
The Istari Agent runs on the machine (or container) and polls the platform for jobs. When a job arrives, the agent executes your module locally. Both the agent and the module must be installed on the same machine.
8.1 Install the Agent
dpkg -i /workspace/downloads/istari-agent_10.1.2_amd64.deb
The agent is installed to /opt/local/istari_agent/.
8.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"
Expected result:
√ Agent configuration saved successfully!
Configuration file: /root/.config/istari_digital/istari_digital_config.yaml
Configuration: Top-level agent section
The agent is now ready for use with the configured settings.
You can verify the config: cat /root/.config/istari_digital/istari_digital_config.yaml
Enable headless mode: Open the config file (path shown above) and add istari_digital_agent_headless_mode: true under the agent: section. This is required when running without a graphical display (e.g. Docker, SSH).
echo " istari_digital_agent_headless_mode: true" >> /root/.config/istari_digital/istari_digital_config.yaml
8.3 Install the Module on the Agent
mkdir -p /opt/local/istari_agent/istari_modules/tutorial-module
cp /workspace/tutorial-module.zip /opt/local/istari_agent/istari_modules/tutorial-module/
unzip -o /opt/local/istari_agent/istari_modules/tutorial-module/tutorial-module.zip -d /opt/local/istari_agent/istari_modules/tutorial-module
8.4 Start the Agent
/opt/local/istari_agent/istari_agent_10.1.2
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 9: Run the Function via Istari
In the Istari UI,
- upload a
.txtfile. - Select the file and
create job - In the
Select a tool/functiondrop down, you should find the tool and function you just created. - Execute the function
This will start a Job that you can monitor in the Jobs section
Check the Agent logs from where you have the Agent 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/tutorial-module with command: ['/opt/local/istari_agent/istari_modules/tutorial-module/python_module', 'FileCopy', '--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...
9.3 Check results
In the Istari UI:
- Go to the Files tab.
- Find your file and view the artefact 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 Istari module wiith 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 seamlessly on the Istari platform.
Key Skills You’ve Learned
- Module Structure: How to organize and scaffold an Istari module.
- 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 Istari Agent to execute jobs.
Next Steps
- Experiment: Try adding more functions (e.g., file transformation, data extraction).
- Automate: Explore workflows using the Istari SDK.
- Scale: Deploy modules for team collaboration or production use.
Troubleshooting
DisplayNameError: Bad display name
The agent tries to start a system-tray icon, which requires a graphical display. In Docker (or SSH / headless servers), there is no display and it 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