The attachments: block lets a workflow declare binary inputs (files,
images) the user provides at launch. Iterion uploads them once,
persists them under the run, and exposes them to nodes via
`` template references. Image
attachments are forwarded to vision-capable agents as native
multimodal ContentBlocks; arbitrary files are exposed as host
filesystem paths plus optional presigned URLs.
This document covers the DSL surface, the runtime semantics across local / desktop / cloud, the upload protocol, and the security model.
attachments:
logo: image
spec: file
description: "Spec PDF that grounds the review"
accept_mime: ["application/pdf"]
required: true
The block is valid at file-level (parallel to vars:) and
workflow-level (inside a workflow <name>: body). Attachment names
must be unique across file and workflow scopes; a duplicate emits
C050 and the later declaration is skipped.
| Field | Required | Notes |
|---|---|---|
<name> |
yes | Identifier referenced via ``. Must not collide with a vars: entry. |
| Type | yes | file or image. image enables multimodal forwarding to claw. |
description |
optional | Surfaced in the Launch modal under the field label. |
accept_mime |
optional | List of type/subtype patterns (* glob allowed). Intersected with the server allowlist. |
required |
optional | Defaults to false. Required attachments block the Launch button until provided. |
| Form | Resolves to |
|---|---|
| `` | host filesystem path (default; same as .path) |
| `` | host filesystem path |
| `` | presigned URL — HMAC-signed local URL or SigV4 S3 URL depending on mode |
| `` | sniffed MIME (e.g. image/png) |
| `` | byte length as a decimal string |
| `` | hex SHA-256 of the upload |
Any other sub-field produces compile-time diagnostic C054. An
unknown attachment name produces C053.
| Code | Meaning |
|---|---|
| C050 | attachment name declared more than once |
| C051 | attachment name collides with a declared vars: entry |
| C052 | accept_mime entry is not in type/subtype form |
| C053 | `` references an undeclared attachment |
| C054 | unknown sub-field after attachments.<name>. (only path, url, mime, size, sha256 are valid) |
The Launch modal uploads each attachment immediately on selection via
POST /api/runs/uploads (multipart/form-data, single file field).
The server returns an upload_id that the launch payload references:
POST /api/runs/uploads
Content-Type: multipart/form-data; boundary=...
# multipart body with `file` field
{
"upload_id": "up_1717169012_aabbccdd",
"original_filename": "logo.png",
"mime": "image/png",
"size": 42184,
"sha256": "…"
}
POST /api/runs
Content-Type: application/json
{
"file_path": "/path/to/workflow.bot",
"attachments": { "logo": "up_1717169012_aabbccdd" }
}
Staged uploads live under <store>/uploads/<upload_id>/ until the
launch promotes them to <store>/runs/<run_id>/attachments/<name>/.
Unreferenced uploads are reaped after one hour (uploadStagingTTL).
The upload handler enforces four limits. In local editor mode,
iterion studio exposes flags for these settings; the cloud
iterion server command currently exposes only its server flags
(port/bind/dir/store-dir/config), so upload limits there use the
server configuration defaults unless an embedder wires explicit
server.Config values.
iterion studio flag |
Default (web/cloud) | Default (desktop) |
|---|---|---|
--max-upload-size |
50 MB | 1 GB |
--max-total-upload-size |
5 × max-upload-size | 5 × max-upload-size |
--max-uploads-per-run |
20 | 20 |
--allow-upload-mime |
safe defaults | safe defaults |
The default MIME allowlist covers image/{png,jpeg,gif,webp},
application/{pdf,json,zip,gzip,x-tar}, text/{plain,markdown,csv},
and application/yaml. The GET /api/server/info endpoint returns
the resolved limits so the SPA can surface them before any byte
leaves the browser.
Errors are mapped to standard codes:
| Status | Cause |
|---|---|
| 413 | upload exceeds the configured per-file or cumulative size limit |
| 415 | sniffed MIME is not in the configured upload MIME allowlist |
| 422 | declared name not present in the workflow’s attachments: |
| 409 | more attachments referenced than the configured per-run limit |
| Mode | Layout |
|---|---|
| Local / desktop | <store>/runs/<run_id>/attachments/<name>/<filename> plus a sidecar meta.json |
| Cloud (S3 / MinIO) | attachments/<run_id>/<name>/<filename> (S3 key); metadata reflected in the runs collection |
The metadata struct (AttachmentRecord) carries name,
original_filename, mime, size, sha256, created_at, and a
storage_ref pointing at the canonical key. It is persisted on
Run.Attachments so resume reads the same data the original launch
saw — there is no special-case retry path.
`` produces:
/api/runs/<id>/attachments/<name>?exp=…&sig=…,
HMAC-signed with a per-store random key. Default TTL 10 minutes.
The signing key lives at <store>/.attachment-signing-key.The bytes endpoint also accepts safe-Origin browser callers (no signature) so the studio SPA can read attachments without minting a URL first.
When the engine starts a run, loadAttachmentInfos reads
Run.Attachments and builds the per-template snapshot consumed by
node prompts and tool commands. The path is the absolute host path
in local mode and /run/iterion/attachments/<name>/<filename> inside
the sandbox (read-only bind mount).
For agent nodes whose backend is claw, the executor:
.path) references where X is declared as image.The blocks land on the Anthropic Messages API as native vision input — no tool call needed.
CLI-based backends cannot accept inline images on stdin. The executor:
read_image tool on the node so the agent can
fetch the bytes itself.The agent is expected to call read_image(path) to load the image
through its own vision pipeline.
When a sandbox is active, the engine appends a read-only bind mount
of the run’s attachments/ directory under
/run/iterion/attachments. Path references resolved inside the
sandbox point there. The mount is read-only by construction: a
malicious agent cannot corrupt the run store.
blob.GetAttachment (S3 /
MinIO) when a node opens an attachment by URL or path. No shared
filesystem is required.pkg/config
or charts/iterion, so it uses code defaults unless a future
deployment wrapper or embedder passes explicit values.image type whenever the file is meant for an LLM’s
vision input — you get free multimodal forwarding to claw without
changing the node definition.file and stream them
to a tool node (cat, unzip, …) rather than interpolating the
whole content into a prompt.accept_mime to lock down what users can upload. The Launch
modal renders the constraint as a <input accept="…"> hint AND
validates client-side before any byte leaves the browser.required: true for attachments the workflow cannot run
without — the Launch button stays disabled until provided.