Files

Upload and download raw bytes in an owned, running container. Both directions require the exact id and createdAt, plus a URL-encoded absolute path.

// Keep apiKey on your server. sandbox is the validated creation identity.
const query = new URLSearchParams({
  id: sandbox.containerId,
  createdAt: sandbox.createdAt,
  path: '/tmp/input.bin',
});
const url = `https://api.mainbrella.com/containers/files?${query}`;
const headers = { Authorization: `Bearer ${apiKey}` };
const upload = await fetch(url, {
  method: 'PUT',
  headers: { ...headers, 'Content-Type': 'application/octet-stream' },
  body: new Uint8Array([0, 128, 255]),
});
if (!upload.ok) throw new Error(`Upload failed: ${upload.status}`);
const download = await fetch(url, { headers });
if (!download.ok) throw new Error(`Download failed: ${download.status}`);
const bytes = new Uint8Array(await download.arrayBuffer());

Paths and writes

Paths must be absolute, at most 4,096 UTF-8 bytes, and contain no NUL, empty, ., or .. segments. Parent directories must already exist; use the capability-gated directory API below or Execute. Paths refer to the guest filesystem; /workspace is a convention, not a security boundary.

GET reads regular files and follows guest symlinks. PUT writes a temporary file beside the destination, then replaces it atomically; directories and existing symlinks are rejected. New files use mode 0600, while replacements preserve permissions.

Directories and metadata

Check the corresponding files flags in capabilities first. These additive routes require the same exact container identity and share the file-operation deadline and pool.

OperationHTTP routeBehavior
ListGET /containers/files/listOne sorted page: entries and nextOffset. Default 100 entries, maximum 1,000.
StatGET /containers/files/statType, size, permissions, owner IDs, modification time and symlink target. Inspects the link itself by default.
Create directoryPOST /containers/files/mkdirJSON {path, recursive?, mode?}; default mode 0700.
RemoveDELETE /containers/files/removeQuery path; nonempty directories require recursive=true. Removes a link itself.
MovePOST /containers/files/moveJSON {path, destination}; destination must be unused.
PermissionsPATCH /containers/files/chmodQuery path, JSON {mode: "0644"}. Rejects leaf symlinks.

Listing rescans between pages, so directory changes can duplicate or omit entries. Root is accepted for list/stat only. Watchers remain unsupported. Recursive removal and moves across filesystems may partly complete before interruption; inspect state before retrying. See the filesystem contract and JavaScript or Python SDK helpers.

Limits and persistence

Uploads and downloads are each limited to 1 MiB. Operations are bounded by 30 seconds and the hard container deadline, share the four-command pool, and renew idle activity. For larger files, use authorized SSH access.

A lost write response may hide a completed upload; read to reconcile before retrying. Export needed files before stopping: workspace storage is ephemeral.