Skip to main content

Local recording and saved evidence uploads

loom run records fresh executions in state.jsonl and events.jsonl under .loom/.runtime/runs/<run-id>/. These JSONL recordings are local-only. Neither web.upload: true nor LOOM_WEB_UPLOAD=true enables their delivery. An enabled upload session reports the local-only limitation without changing the local result or exit code. Inspect the exact returned ID with loom logs <run-id> or the MCP run and event readers.

Explicit upload preparation and retry support retained historical evidence from earlier producers. They do not convert a fresh JSONL run into a native capture. The following destination, credential and transfer rules apply to those saved formats.

Use loom web setup project to save the repository's complete, non-secret web tuple and local enrollment. The web.upload setting and LOOM_WEB_UPLOAD describe automatic-upload consent for producers that support it; they do not add upload support to current JSONL runs. Any explicitly supplied LOOM_WEB_UPLOAD value other than true disables automatic upload, including uploader credential lookup and network access. Authentication alone does not enable uploads.

With upload enabled, any of LOOM_WEB_URL, LOOM_WEB_PROJECT_ID, or LOOM_WEB_TOKEN_ENV selects the legacy CI mode. It requires LOOM_WEB_UPLOAD=true and a complete validated URL, project UUID, token variable name, and token value. A partial CI tuple fails; no field comes from repository configuration or the local credential store. With no legacy selectors, the repository's complete version-1 web tuple must match a local enrollment for the canonical repository root, origin, project, and upload choice. A copied repository file alone cannot authorize credentials.

Saved-evidence delivery binds its destination, enrollment and selected credential before transmission. A still-valid selected project token is used without refreshing an account grant. An expired CLI-issued token can be renewed only by naming and proving possession of that predecessor; the Web server must confirm expiry and lineage. Manual, revoked, unsafe, missing-selected, or ambiguous credentials require explicit reconnection. A lost issuance or renewal response leaves a durable recovery marker; the CLI does not issue again with a new operation ID. Explicit retry requires an already selected eligible project credential and cannot make an initial grant.

Only a project token reaches ingestion. A 401 or 403 stops the upload without retrying under another credential and marks the selected credential as rejection-unknown. A later run may attempt only the named, server-verified expiry renewal when eligible; otherwise run loom auth project to reconnect. The CLI never opens a browser or device flow during upload.

Upload warnings identify a bounded stage and reason with a next step. Configuration and enrollment failures call for checking the repository destination; private-store failures call for inspecting storage; interrupted issuance or renewal calls for reviewing loom auth project status and Web credential history before another approval. Transport or timeout failures do not imply the selected credential was rejected. Server response text and credential material are never included in these warnings. The status command distinguishes unresolved historical operations from operations that block the currently selected credential.

Current local recording masks values resolved from workflow secret declarations before persistence. With an upload session enabled, values already collected while binding that session also protect JSONL recording and console output before preparation starts: the selected project token, relevant already loaded account values, and values explicitly named by LOOM_WEB_REDACT_ENV. This protection does not enable delivery. It persists through cleanup and upload configuration failures, performs no additional credential or environment discovery, and does not inject those values into jobs. Disabled upload performs no uploader credential lookup. Saved-evidence upload audits frozen originals and rejects delivery when a known value remains; it never rewrites originals or retry bytes. Unknown, job-generated, or transformed secrets remain outside exact-match protection. The private account store remains accessible to other trusted workflows running as the same operating-system user; it is not a process sandbox. Account store handles are not passed to jobs or recording.

Retained historical native captures​

Earlier producers may have retained native hierarchies under .loom/.runtime/runs/<run-directory>/. Their run descriptor, immutable history, checkpoint inventories, original event batches, protected output segments and final seal supply native authority. The uploader verifies identities, references, hashes and closure using the same reader as local inspection. It does not remap that historical evidence into another receipt format. Current two-file JSONL directories do not supply this native authority.

Authenticated capabilities must match loom.native-file-transfer.v1, loom.native-run-artifacts.v1, the exact pinned schema digest and SHA-256, and advertise required file kinds and file/run limits. Native transfer uses separate /api/native/v1 routes. Historical receipt-only Web deployments are unsupported for these runs; retained files stay pending without downconversion.

Exact file bytes are sent with their original paths, UUIDs, lengths and hashes. Each accepted or duplicate acknowledgement must bind those same fields. The private ledger under .loom/.runtime/native-uploads/<run-directory>/<attempt-UUID>/ contains immutable selection/destination bindings and per-file acknowledgements. It contains no base64 evidence bundle and no credentials. Source files must remain available for retry. A conflict or changed destination stops delivery.

Metadata, events and protected output are supported file kinds. Arbitrary artifact bodies remain local by default. Explicit LOOM_WEB_NATIVE_ARTIFACT_BODIES=true and the server's advertised body authorization are both required to send them. Excluded bodies remain pending; metadata acknowledgements cannot prove a complete remote inventory. Locally partial evidence requires the server's partial-evidence capability and remains partial after transport.

Concurrent deliveries share bounded project requests, bytes and concurrency. Transient failures use bounded backoff respecting Retry-After; exhausted budgets retain exact originals and acknowledged progress. Success means native files acknowledged, without an independently verified complete receiver state. Historical v1/v2/v3 receipt upload bundles remain immutable and keep their version-specific retry route.

Delivery timing​

SettingRepository configEnvironmentFlagDefault
Retry intervalweb.uploadRetryIntervalLOOM_WEB_UPLOAD_RETRY_INTERVAL--upload-retry-interval10s
Total budgetweb.uploadTimeoutLOOM_WEB_UPLOAD_TIMEOUT--upload-timeout120s

Both loom run and loom web upload retry accept the flags. Each field resolves independently: an explicitly supplied flag wins over a present environment value, which wins over repository config, then the default. Empty or invalid winning values fail without fallback. Use a positive integer followed by s, m or h; the resulting duration must be between 1s and 1h inclusive, with interval no greater than timeout. Version-1 configs may omit both fields. Project setup preserves optional timing values.

Timing settings never enable fresh JSONL uploads. Explicit saved-evidence delivery does not execute jobs or change their recorded result and exit code. Credential refresh or eligible named renewal and delivery share the total budget. A request gets at most five seconds, further bounded by remaining time. Retryable failures repeat until the deadline, waiting max(interval, valid Retry-After). A server's 60-second delay fits the default budget when enough time remains. If the next wait leaves no usable request time, the CLI stops incomplete. Cancellation and terminal authentication, conflict, unsupported-version, bad-data or quota errors stop delivery promptly.

Prepare a finalized run offline​

Select the native run directory from its original repository or a directory beneath it:

This command accepts retained historical native captures, not current JSONL run directories. It does not synthesize missing native authority or rerun jobs.

loom web upload prepare --run-id 20261005T120000Z-build-abcdefgh

Use the actual directory name printed by your run. Preparation runs zero jobs and makes no network, environment or credential reads. It validates the native originals and saves a private immutable selection binding in .loom/.runtime/native-uploads/<run-directory>/<attempt-UUID>/selection.json. No evidence bodies are copied. Preparation preserves the existing run/attempt identities and exact bytes; it does not issue authority, change consent or claim delivery.

The selector is one explicit safe run directory name. There is no latest-run scan, arbitrary path or workflow resolution. Unsafe, changing, conflicting or missing authority fails without a sibling-receipt fallback. Success reports the native run UUID, existing attempt UUID as the upload ID, file count and declared evidence state. Keep the original hierarchy for retry.

Retry one saved upload​

Run from the original enrolled repository or a directory beneath it:

loom web upload retry \
--run-id 20261005T120000Z-build-abcdefgh \
--upload-id 22222222-2222-4222-8222-222222222222 \
--upload-retry-interval 10s --upload-timeout 3m

Both selectors are required. The native run directory name selects exactly one rooted hierarchy; the upload ID must equal its attempt UUID. Retry revalidates the originals, negotiates the pinned capability and sends only files without durable acknowledgements. It executes zero jobs, never remasks files or replaces identities, and retains progress after interruption. If an acknowledgement was lost, an exact duplicate transfer is safe. Invalid hashes, identity conflicts or changed bindings stop delivery.

Explicit retry authorizes only this invocation, even with web.upload: false or LOOM_WEB_UPLOAD=false; it does not change future-run consent. The retained origin/project must match the current authorized tuple. Stored mode requires the exact repository enrollment and an already selected eligible project credential. Retry cannot make an initial grant or fall back to broader credentials. CI mode requires the complete existing destination/token tuple. Credential renewal and transport share the delivery budget.

Historical saved uploads still use their original loom-run-local-N selector and UUID. Receipt bundles under .loom/.runtime/logs/.../web-upload/ or .loom/.runtime/canonical-uploads/ are strictly validated and replayed in their original versions; they are never relabeled as native files. No historical bundle is overwritten by native preparation or retry.