Alloy
Mesh StoragePython SDK

Upload Files

Upload MCAP recordings and metadata sidecars from Python

Use alloy.storage when you already have files on disk and want them to land directly in Mesh Storage.

from alloy import storage

with storage.connect() as store:
    store.upload_folder("local/run-001", path="flights/run-001")

Folder uploads

upload_folder(local_folder, path=...) walks the folder recursively and preserves relative paths below that folder.

local/run-001/
├── metadata.json
├── drone-a/
│   └── flight.mcap
└── drone-b/
    └── flight.mcap
from alloy import storage

with storage.connect() as store:
    result = store.upload_folder(
        "local/run-001",
        path="flights/run-001",
        overwrite=False,
    )

print(result.prefix)
# uploads/sdk-uploads/flights/run-001/

The resulting Mesh keys are:

uploads/sdk-uploads/flights/run-001/metadata.json
uploads/sdk-uploads/flights/run-001/drone-a/flight.mcap
uploads/sdk-uploads/flights/run-001/drone-b/flight.mcap

Single-file uploads

upload_file(local_path, path=...) treats path as a folder and uploads the local file into that folder using the local basename.

from alloy import storage

with storage.connect() as store:
    result = store.upload_file(
        "local/run-001/flight.mcap",
        path="flights/run-001",
        overwrite=False,
    )

print(result.files[0].key)
# uploads/sdk-uploads/flights/run-001/flight.mcap

Path rules

path is a Mesh folder below the SDK upload root:

uploads/sdk-uploads/<path>/

Pass this:

flights/run-001

Do not pass:

uploads/sdk-uploads/flights/run-001
uploads/flights/run-001
s3://bucket/flights/run-001
flights/run-001/

If you omit path, Alloy generates a dated folder such as:

2026-06-24/4c9033de-6e7d-4d9a-8c34-9b4ab10e6bb7

The upload result includes the concrete generated path and prefix.

Supported files

The high-level upload helpers support:

File typeExtension
MCAP recordings.mcap
JSON sidecars.json
YAML sidecars.yml, .yaml

Metadata sidecars upload before MCAP files. This makes sidecar metadata available to the ingestion path before the recording is processed.

Overwrite behavior

overwrite=False is the only supported high-level SDK mode in v1.

from alloy import storage

with storage.connect() as store:
    store.upload_folder("local/run-001", path="flights/run-001", overwrite=False)

Before uploading, the SDK checks every target key. If any key already exists, it raises AlloyStorageConflictError before uploading the new batch.

from alloy import storage

try:
    storage.upload_file("flight.mcap", path="flights/run-001", overwrite=False)
except storage.AlloyStorageConflictError as exc:
    print(exc)

overwrite=True raises immediately. Delete or replace files through the Mesh deletion workflow before uploading a replacement.

What happens after upload

Uploaded files appear in Mesh Storage under uploads/sdk-uploads/. Each MCAP goes through the normal lifecycle:

  1. Queued - the file has landed and is waiting for processing
  2. Processing - Alloy is parsing the MCAP and building queryable tables
  3. Ready - SQL and external query paths can read the processed data
  4. Failed - processing failed; check the file row for details

Replay and Inspect work as soon as the MCAP lands. SQL requires the file to be Ready.

One-shot helpers

For short scripts, use module-level helpers. They open and close a client for one operation.

from alloy import storage

storage.upload_folder("local/run-001", path="flights/run-001")
storage.upload_file("local/run-001/flight.mcap", path="flights/run-001")

Async one-shots are available too:

from alloy import storage

await storage.async_upload_folder("local/run-001", path="flights/run-001")
await storage.async_upload_file("local/run-001/flight.mcap", path="flights/run-001")

Use a long-lived client when you are doing more than one operation.

On this page