Skip to content

Cloud Storage

Access S3-compatible object storage. Upload, download, list, and manage files with presigned URL support.

For storage configuration, see Cloud Storage.

local cloudstorage = require("cloudstorage")

Get a cloud storage resource by registry ID:

local storage, err = cloudstorage.get("app.infra:files")
if err then
return nil, err
end
storage:upload_object("data/file.txt", "content")
storage:release()
ParameterTypeDescription
idstringStorage resource ID

Returns: Storage, error

Upload content from string or file:

local storage = cloudstorage.get("app.infra:files")
-- Upload string content
local ok, err = storage:upload_object("reports/daily.json", json.encode({
date = "2024-01-15",
total = 1234
}))
-- Upload from file
local fs = require("fs")
local vol = fs.get("app:data")
local file = vol:open("/large-file.bin", "r")
storage:upload_object("backups/large-file.bin", file)
file:close()
storage:release()
ParameterTypeDescription
keystringObject key/path
contentstring or ReaderContent as string or file reader
optionstableOptional metadata and conditional write options

Returns: boolean, error

Attach metadata or guard the write with an options table:

storage:upload_object("reports/daily.json", body, {
content_type = "application/json",
cache_control = "max-age=3600",
metadata = { owner = "team-a", run_id = "1234" }, -- stored as x-amz-meta-*
only_if_absent = true -- fail if the key already exists
})
OptionTypeDescription
content_typestringMIME type
cache_controlstringCache-Control header
content_dispositionstringContent-Disposition header
content_encodingstringContent-Encoding header
metadatatableUser metadata (string keys/values), stored as x-amz-meta-*
headerstableAdditional request headers (string keys/values)
if_matchstringWrite only if the current object ETag matches
if_none_matchstringWrite only if no object matches the ETag ("*" means any)
only_if_absentbooleanWrite only if the key does not exist (alias for if_none_match = "*")

A conditional write that fails its precondition returns a precondition_failed error.

Download an object to a file writer:

local storage = cloudstorage.get("app.infra:files")
local fs = require("fs")
local vol = fs.get("app:temp")
local file = vol:open("/downloaded.json", "w")
local ok, err = storage:download_object("reports/daily.json", file)
file:close()
-- Download partial content (first 1KB)
local partial = vol:open("/partial.bin", "w")
storage:download_object("backups/large-file.bin", partial, {
range = "bytes=0-1023"
})
partial:close()
storage:release()
ParameterTypeDescription
keystringObject key to download
writerWriterDestination file writer
options.rangestringByte range (e.g., “bytes=0-1023”)
options.if_matchstringDownload only if the object ETag matches
options.if_none_matchstringDownload only if the ETag does not match

Returns: boolean, error

A failed precondition (if_match/if_none_match) returns a precondition_failed error.

List objects with optional prefix filtering:

local storage = cloudstorage.get("app.infra:files")
local result, err = storage:list_objects({
prefix = "reports/2024/",
max_keys = 100
})
for _, obj in ipairs(result.objects) do
print(obj.key, obj.size, obj.etag)
end
-- Paginate through large results
local token = nil
repeat
local result = storage:list_objects({
prefix = "logs/",
max_keys = 1000,
continuation_token = token
})
for _, obj in ipairs(result.objects) do
process(obj)
end
token = result.next_continuation_token
until not result.is_truncated
storage:release()
ParameterTypeDescription
options.prefixstringFilter by key prefix
options.max_keysintegerMaximum objects to return
options.continuation_tokenstringPagination token
options.include_ownerbooleanInclude each object’s owner (id, display_name)
options.include_versionsbooleanList object versions; each item includes version_id

Returns: table, error

Result contains objects, is_truncated, next_continuation_token. Each object has key, size, etag, storage_class, and optional last_modified, version_id, and owner.

In list results content_type is always empty — S3 list operations do not return it. Use head_object to read an object's content type and metadata.

Fetch a single object’s metadata without downloading its body:

local storage = cloudstorage.get("app.infra:files")
local meta, err = storage:head_object("reports/daily.json")
if err then
return nil, err
end
print(meta.size, meta.etag, meta.content_type)
for k, v in pairs(meta.metadata) do
print("meta", k, v)
end
storage:release()
ParameterTypeDescription
keystringObject key

Returns: table, error

Result fields:

FieldTypeDescription
sizeintegerObject size in bytes
etagstringEntity tag
content_typestringMIME type
cache_controlstringCache-Control header
content_dispositionstringContent-Disposition header
content_encodingstringContent-Encoding header
storage_classstringStorage class
version_idstringVersion ID (present when versioning is enabled)
last_modifiedintegerLast modified time (Unix seconds)
metadatatableUser metadata (x-amz-meta-*)
headerstableRaw response headers (lowercased keys)

A missing object returns a not_found error.

Remove multiple objects:

local storage = cloudstorage.get("app.infra:files")
storage:delete_objects({
"temp/file1.txt",
"temp/file2.txt",
"temp/file3.txt"
})
storage:release()
ParameterTypeDescription
keysstring[]Array of object keys to delete

Returns: boolean, error

Create a temporary URL that allows downloading an object without credentials. Useful for sharing files with external users or serving content through your application.

local storage, err = cloudstorage.get("app.infra:files")
if err then
return nil, err
end
local url, err = storage:presigned_get_url("reports/quarterly.pdf", {
expiration = 3600
})
storage:release()
if err then
return nil, err
end
-- Return URL to client for direct download
return {download_url = url}
ParameterTypeDescription
keystringObject key
options.expirationintegerSeconds until URL expires (default: 3600)

Returns: string, error

Create a temporary URL that allows uploading an object without credentials. Enables clients to upload files directly to storage without proxying through your server.

local storage, err = cloudstorage.get("app.infra:files")
if err then
return nil, err
end
local url, err = storage:presigned_put_url("uploads/user-123/avatar.jpg", {
expiration = 600,
content_type = "image/jpeg",
content_length = 1024 * 1024
})
storage:release()
if err then
return nil, err
end
-- Return URL to client for direct upload
return {upload_url = url}
ParameterTypeDescription
keystringObject key
options.expirationintegerSeconds until URL expires (default: 3600)
options.content_typestringRequired content type for upload
options.content_lengthintegerMaximum upload size in bytes

Returns: string, error

MethodReturnsDescription
upload_object(key, content, opts?)boolean, errorUpload string or file content
download_object(key, writer, opts?)boolean, errorDownload to file writer
head_object(key)table, errorFetch object metadata
list_objects(opts?)table, errorList objects with prefix filter
delete_objects(keys)boolean, errorDelete multiple objects
presigned_get_url(key, opts?)string, errorGenerate temporary download URL
presigned_put_url(key, opts?)string, errorGenerate temporary upload URL
release()booleanRelease storage resource

Cloud storage operations are subject to security policy evaluation.

ActionResourceDescription
cloudstorage.getStorage IDAcquire a storage resource
ConditionKindRetryable
Empty resource IDerrors.INVALIDno
Resource not founderrors.NOT_FOUNDno
Not a cloud storage resourceerrors.INVALIDno
Storage releasederrors.INVALIDno
Empty keyerrors.INVALIDno
Content nilerrors.INVALIDno
Writer not validerrors.INVALIDno
Object not founderrors.NOT_FOUNDno
Conditional precondition failederrors.CONFLICTno
Permission deniederrors.PERMISSION_DENIEDno
Operation failederrors.INTERNALno

See Error Handling for working with errors.