S3 API
S3-compatible object storage helpers are available under ptool.s3 and p.s3.
ptool.s3.connect
v0.10.0- Introduced.
ptool.s3.connect(options) opens an S3-compatible object storage connection
and returns a Connection object.
options fields:
bucket(string, required): The bucket name.region(string, optional): The AWS region or provider region.endpoint(string, optional): A custom S3-compatible endpoint URL such as MinIO, R2, or another object storage service.access_key_id(string, optional): The access key ID.secret_access_key(string, optional): The secret access key.session_token(string, optional): The session token.root(string, optional): A root prefix applied to all object operations.allow_anonymous(boolean, optional): Whentrue, allow unsigned requests if credentials are not configured. Defaults tofalse.
Environment fallback:
- Explicit
optionsvalues win. - Missing
region,endpoint,access_key_id,secret_access_key, andsession_tokenvalues fall back to:AWS_REGIONAWS_ENDPOINT,AWS_ENDPOINT_URL, orAWS_S3_ENDPOINTAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_SESSION_TOKEN
- Environment fallback uses
ptool's runtime environment view, so values set throughp.os.setenv(...)are also visible toptool.s3.connect(...).
Example:
local s3 = ptool.s3.connect({
bucket = "artifacts",
region = "auto",
endpoint = "https://<account>.r2.cloudflarestorage.com",
access_key_id = p.os.getenv("AWS_ACCESS_KEY_ID"),
secret_access_key = p.os.getenv("AWS_SECRET_ACCESS_KEY"),
root = "builds/",
})
Connection
v0.10.0- Introduced.
Connection represents an open object storage connection returned by
ptool.s3.connect().
It is implemented as a Lua userdata.
Methods:
conn:read(path[, options])->stringconn:write(path, content[, options])->tableconn:delete(path)->nilconn:exists(path)->booleanconn:list([prefix])->tableconn:stat(path)->table
Path rules:
- Object paths must be non-empty strings unless otherwise noted.
- Leading
/is ignored, so/foo/bar.txtandfoo/bar.txttarget the same object. - Paths are relative to
rootwhenrootis configured on the connection.
Entry table shape:
path(string): The object path relative to the connection root.size(integer): The object size in bytes.etag(string | nil): The object ETag when available.last_modified(string | nil): The last-modified timestamp when available.content_type(string | nil): The object content type when available.version(string | nil): The object version when available.metadata(table | nil): User-defined object metadata when available.is_file(boolean): Whether the entry is a file.is_dir(boolean): Whether the entry is a directory.mode(string): One of"file","dir", or"unknown".
read
v0.10.0- Introduced. Unreleased - Changed.
Canonical API name: ptool.s3.Connection:read.
conn:read(path[, options]) reads an object as raw bytes and returns a Lua string.
path(string, required): The object path.options(table, optional): Read options.- Returns:
string.
options fields:
range(table, optional): Reads a byte range using half-open bounds[start, end).start(integer, optional): The first byte offset to include.end(integer, optional): The first byte offset to exclude.
Behavior:
- Omitting
rangereads the full object. { start = N }reads from byteNto the end.{ end = N }reads from the beginning up to, but not including, byteN.{ start = A, end = B }reads bytesAthroughB - 1.
Example:
local s3 = ptool.s3.connect({ bucket = "artifacts" })
local content = s3:read("releases/v1.0.0/notes.txt")
print(content)
local prefix = s3:read("releases/v1.0.0/notes.txt", {
range = { start = 0, end = 5 },
})
print(prefix)
write
v0.10.0- Introduced. Unreleased - Changed.
Canonical API name: ptool.s3.Connection:write.
conn:write(path, content[, options]) writes a Lua string to an object as raw
bytes and returns an entry table.
path(string, required): The object path.content(string, required): The bytes to upload.options(table, optional): Write options.- Returns:
table.
options fields:
content_type(string, optional): Sets the object content type.cache_control(string, optional): Sets the object cache-control header.content_disposition(string, optional): Sets the object content-disposition header.content_encoding(string, optional): Sets the object content-encoding header.metadata(table, optional): Sets user-defined metadata as string key/value pairs.if_not_exists(boolean, optional): Write only when the object does not already exist. Defaults tofalse.if_match(string, optional): Write only when the current ETag matches.if_none_match(string, optional): Write only when the current ETag does not match.
Behavior:
contentis uploaded byte-for-byte.- Embedded NUL bytes and non-UTF-8 bytes are preserved.
- The returned entry avoids an immediate follow-up
stat()call for common metadata. - Some S3-compatible services may not echo user-defined
metadataoretagin the write response. When that happens, those fields remainniluntil a laterstat().
Example:
local s3 = ptool.s3.connect({ bucket = "artifacts" })
local entry = s3:write("tmp/hello.txt", "hello\n", {
content_type = "text/plain; charset=utf-8",
metadata = { author = "ptool" },
})
print(entry.path, entry.etag, entry.version)
s3:write("tmp/blob.bin", "\x00\xffABC")
delete
v0.10.0- Introduced.
Canonical API name: ptool.s3.Connection:delete.
conn:delete(path) deletes an object.
path(string, required): The object path.
exists
v0.10.0- Introduced.
Canonical API name: ptool.s3.Connection:exists.
conn:exists(path) checks whether an object exists.
path(string, required): The object path.- Returns:
boolean.
list
v0.10.0- Introduced.
Canonical API name: ptool.s3.Connection:list.
conn:list([prefix]) lists entries under a prefix and returns a dense Lua
array table.
prefix(string, optional): The prefix to list. Defaults to the connection root.- Returns:
table.
Example:
local s3 = ptool.s3.connect({ bucket = "artifacts", root = "builds/" })
local entries = s3:list("2026/")
for _, entry in ipairs(entries) do
print(entry.path, entry.mode, entry.size)
end
stat
v0.10.0- Introduced.
Canonical API name: ptool.s3.Connection:stat.
conn:stat(path) returns metadata for a single object.
path(string, required): The object path.- Returns:
table.
Example:
local s3 = ptool.s3.connect({ bucket = "artifacts" })
local meta = s3:stat("releases/v1.0.0/app.tar.zst")
print(meta.size, meta.etag, meta.last_modified)