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.mcapfrom 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.mcapSingle-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.mcapPath rules
path is a Mesh folder below the SDK upload root:
uploads/sdk-uploads/<path>/Pass this:
flights/run-001Do 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-9b4ab10e6bb7The upload result includes the concrete generated path and prefix.
Supported files
The high-level upload helpers support:
| File type | Extension |
|---|---|
| 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:
- Queued - the file has landed and is waiting for processing
- Processing - Alloy is parsing the MCAP and building queryable tables
- Ready - SQL and external query paths can read the processed data
- 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.