Skip to content

Uploads & downloads

Understanding proxied transfers, direct grants, and multipart uploads.

LoonFS exposes two content-transfer paths:

  • Proxied — content is routed through the LoonFS server.
  • Direct — LoonFS shares a short-lived object-store presigned URL and the client transfers bytes directly.

The proxy path is conceptually simpler, but a busy LoonFS server can saturate the nodes network. The reference server defaults to a 256 MiB limit for each proxied transfer, with 8 concurrent uploads and 16 concurrent downloads. (These are server-path limits, not maximum file sizes.)

Direct transfer support and available checksum algorithms depend on the storage provider:

Storage providerDirect GETDirect PUTDirect multipart
Amazon S3✓✓✓
Cloudflare R2✓✓✓
Google Cloud Storage✓✓—
Azure Blob Storage———

Other S3-compatible stores are treated conservatively by default and use proxied transfers until the full capability set is verified.

When filesystem.commits.inline_content is advertised, small files can be sent directly in a commit using base64 inline_content, without an upload session or content token:

{
"commit_id": "c_hello_01",
"operations": [
{ "kind": "put_file", "path": "/hello.txt", "inline_content": "aGVsbG8K" }
]
}

This writes hello followed by a newline. Remember to include the required Loonfs-Actor header. On retries, use the same actor, commit ID, and body. Each inline value must fit commit.max_inline_content_bytes_per_operation after decoding (64 KiB by default). Supply exactly one of inline_content and content_ref per content-writing operation; use an upload session for larger files.

Session-based uploads begin with Create upload and finish with Complete upload. Creation, content staging, completion, and status reads return the same UploadSession shape, discriminated by status (open, completed, or aborted). A completed session includes content_ref and a short-lived content_token while its token minting window remains open.

Pass the returned content_ref unchanged to put_file, including its owner_namespace_id, and supply the token in the commit request’s content_tokens list as proof that the content is durable and the namespace may include it. Completing an upload does not create a file; the commit publishes it into the filesystem.

An unused session can be aborted and clients can poll session status to mint another upload URL while the receipt window remains open.

Begin a service-proxied upload with:

{ "mode": "service_proxied" }

Send the bytes to Upload content, then complete the session by naming the mode:

{ "mode": "service_proxied" }

The completion response will return the durable content_ref and the content_token.

Use direct_put when the complete size is known and the object fits within upload.direct_put.max_content_bytes. Begin the session with that size; the server answers with the checksum algorithm it expects at completion (these vary by store provider):

{
"mode": "direct_put",
"size_bytes": 1234
}

The response returns the accepted checksum_algorithm and a short-lived access URL. Send the exact bytes to the returned URL using its suggested method and every supplied header. After the object-store request succeeds, complete the LoonFS upload with the size and checksum of what you sent:

{
"mode": "direct_put",
"content": {
"size_bytes": 1234,
"checksum": {
"algorithm": "sha256",
"value": "<64 lowercase hex characters>"
}
}
}

LoonFS automatically verifies the durable object before publishing the content_ref.

Use direct_multipart for large content if supported by the deployment. Omit part_size_bytes to accept the server’s part size, or request one explicitly:

{
"mode": "direct_multipart",
"part_size_bytes": 8388608
}

The response supplies the accepted part size and checksum algorithm. For each part:

  1. Compute the required checksum.

  2. Ask Sign upload parts for access:

    {
    "parts": [
    {
    "part_number": 1,
    "checksum": {
    "algorithm": "crc64nvme",
    "value": "<16 lowercase hex characters>"
    }
    }
    ]
    }
  3. Upload with the returned access request and record the provider’s ETag and the checksum. (Requesting access for the same part again is the retry mechanism.)

Complete with the checksum and size of the assembled object plus every part in ascending order:

{
"mode": "direct_multipart",
"content": {
"size_bytes": 12582912,
"checksum": {
"algorithm": "crc64nvme",
"value": "<16 lowercase hex characters>"
}
},
"parts": [
{
"part_number": 1,
"etag": "<provider ETag>",
"checksum": {
"algorithm": "crc64nvme",
"value": "<16 lowercase hex characters>"
}
},
{
"part_number": 2,
"etag": "<provider ETag>",
"checksum": {
"algorithm": "crc64nvme",
"value": "<16 lowercase hex characters>"
}
}
]
}

Use Create download to obtain a presigned object-store request. Send path and an optional revision_no or snapshot_id in the JSON body. This requires filesystem.downloads.direct_get; it does not return a proxied fallback. If direct downloads are unsupported, use Get file bytes within download.service_proxied.max_content_bytes. A download of inline content can return content_not_materialized if the deployment cannot materialize the object (for example, if the server has read-only access to the bucket); use the proxied route or retry after maintenance. A request cannot combine snapshot_id with revision_no; the snapshot already selects the revision. See Snapshots.

When stable identity matters, use Begin download by inode or Get file revision bytes by inode. These endpoints name an exact inode and revision even if its name or path changes.

loonfs put and loonfs get will choose a transport automatically and show progress on stderr; --no-progress disables it.

  • -r recursively traverses directory trees with bounded concurrency and commits each file separately.
  • Downloads resume from a verified partial file when the transport is streamable; server-proxied downloads restart. Remote uploads resume only when they use direct multipart and the payload is larger than 8 MiB.
  • See also Transfers.