Skip to content

Materialize Files

A materialize node delivers workflow-authored files to the agent filesystem without placing their rendered bodies in the step response. Moira issues a short-lived archive command, pauses the execution, and advances only after the agent runs the command and submits an empty completion.

Use this node for stable files owned by the workflow definition, such as instructions, standards, or empty directory skeletons. Files whose contents depend on the agent’s analysis remain the responsibility of the agent-directive that produces them.

Store reusable text in a string entry of variableRegistry, then reference it with from:

{
"variableRegistry": {
"workspace_reference": {
"type": "string",
"description": "Instructions delivered to the workspace",
"default": "# Workspace reference\n\nFollow the project contract."
}
},
"nodes": [
{
"id": "materialize-workspace",
"type": "materialize",
"basePath": "{{workspace_path}}",
"files": [
{ "path": "reference.md", "from": "workspace_reference" },
{ "path": "plans/.keep", "content": "" }
],
"connections": {
"success": "work",
"error": "materialize-failed"
}
}
]
}
PropertyRequiredContract
basePathYesTemplated destination directory; its rendered value must be non-empty and contain no NUL character
filesYesBetween 1 and 100 archive entries
files[].pathYesTemplated safe path relative to basePath
files[].fromOne ofName of a string registry entry whose current default supplies the file body
files[].contentOne ofMust be exactly ""; creates an empty skeleton file
connections.successYesSuccessor after an empty completion input
connections.errorNoRoute for a presentation-time validation, configuration, database, or grant-issuance error

Every file must declare exactly one of from and content. Non-empty inline content is rejected; use one registry entry as the source of truth instead.

When the node is presented, Moira renders basePath and the path summary, creates a five-minute grant, and returns a POSIX command with every argument already shell-quoted:

Terminal window
mkdir -p -- '<basePath>' && curl -sSf -- '<one-use-url>' | tar -x -C '<basePath>'

Run the emitted command exactly. The URL is an opaque bearer credential: do not reconstruct, edit, log, or share it. After extraction succeeds, complete the step with null or {}. No other input shape is accepted.

Calling session({ action: "current_step" }) while the execution is paused issues a fresh command and grant without advancing the graph. Completing the step does not prove that extraction happened, so make the successor verify any file that is required for its work.

Moira reloads the current workflow when the URL is requested. It renders each path and each registry-backed body using the execution context bound to the grant, then creates an uncompressed tar archive. An edit made after the command was issued can therefore change archive paths or contents, but cannot change the destination embedded in that command.

Archive entries contain only paths relative to basePath; the destination itself is not included. Both declared and rendered paths must be normalized, non-empty, relative, and unique. Moira rejects NUL characters, absolute or backslash-rooted paths, empty segments, and . or .. segments.

Resource limits are enforced on rendered UTF-8 content:

  • no more than 100 files;
  • no more than 1 MiB per file;
  • no more than 10 MiB total uncompressed content.

Files are emitted with mode 0644. The engine validates basePath, but it does not confine the destination to a project directory. Workflow authors must derive it from a trusted workspace path, and agents must inspect the emitted destination before running the command.

The five-minute grant is stored server-side and bound to the current user, execution, and node. The execution must still be running and waiting at that same materialize node. A valid grant is claimed atomically only after the archive has been rendered successfully, so it can be used for exactly one successful download. Request logging redacts the credential from the materialize URL.

FailureResult
Invalid node definition, rendered destination, configuration, database access, or grant issuance while presenting the stepFollow connections.error when present; otherwise the execution surfaces the error
Invalid, expired, already used, or incorrectly bound URLHTTP 401 with Invalid or expired materialize token
Invalid rendered archive path, missing registry source, template failure, or size-limit violationHTTP 400 with Materialize archive could not be generated; the grant is not claimed
Local curl, pipe, filesystem, or tar failureThe agent reports the blocker and does not complete the step

Workflow Management Flow resolves the workspace once, then materializes its stable bootstrap files before routing to create or edit work:

get-action-type
-> materialize-workspace-bootstrap
-> route-action-type
| create -> gather-workflow-requirements
| edit -> prepare-edit-workflow

Its materialize declaration is equivalent to:

{
"id": "materialize-workspace-bootstrap",
"type": "materialize",
"basePath": "{{workspace_path}}",
"files": [
{ "path": "process-id.txt", "from": "workspace_process_id_file" },
{ "path": "workflow-authoring-reference.md", "from": "workflow_authoring_reference" }
],
"connections": { "success": "route-action-type" }
}

The preceding owner writes the global workspace_path. Registry defaults supply the execution ID and the authoring reference. Later create and edit owners write dynamic requirements, provenance, plans, and review reports; they do not rewrite these stable bootstrap files.

To replace an agent directive that manually writes stable workflow-authored files:

  1. Move each stable body into one string variableRegistry default.
  2. Have an existing early responsibility return the trusted global path used by basePath.
  3. Insert one materialize node after that owner and before the first consumer.
  4. Remove only the corresponding static-file instructions from later directives. Keep dynamic file creation with the responsibility that determines the content.
  5. Validate the workflow and test the emitted command, archive entries, rendered bodies, and the success and failure routes.