Object Storage
Create a bucket
Section titled “Create a bucket”POST /v1/object-storage/buckets · scope objectstorage:write · reversible · idempotent
Returns the same resource as GET /v1/object-storage/buckets/{bucket}. The backend’s create call responds with the raw registry row —a different shape, with a different date format— so it is re-read before responding: it costs one call and buys create and read returning the same object.
curl https://api.truo.cloud/v1/object-storage/buckets \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"backups"}'truo object-storage bucket create <name>await truo.objectstorage.buckets.create({"name":"backups"});truo_objectstorage({ "action": "bucket_create"})operationId: objectstorage.buckets.create
Delete a bucket
Section titled “Delete a bucket”DELETE /v1/object-storage/buckets/{bucket} · scope objectstorage:write · destructive — cannot be undone · idempotent
A bucket with objects is not deleted: the request fails and touches nothing. ?purge=true deletes it along with all its contents, and that cannot be undone — there is no trash bin and no versioning. To know how many objects will be lost, empty it first with POST .../empty, which returns the count.
curl https://api.truo.cloud/v1/object-storage/buckets/backups \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo object-storage bucket delete backupsawait truo.objectstorage.buckets.delete("backups");truo_objectstorage({ "action": "bucket_delete", "bucket": "backups"})operationId: objectstorage.buckets.delete
Empty a bucket
Section titled “Empty a bucket”POST /v1/object-storage/buckets/{bucket}/empty · scope objectstorage:write · destructive — cannot be undone · asynchronous · idempotent
Deletes every object and keeps the bucket with its configuration. It cannot be undone. On a large bucket it can take a while: deletion runs object by object against the storage.
curl https://api.truo.cloud/v1/object-storage/buckets/backups/empty \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo object-storage bucket empty backupsawait truo.objectstorage.buckets.empty("backups");truo_objectstorage({ "action": "bucket_empty", "bucket": "backups"})operationId: objectstorage.buckets.empty
Get a bucket
Section titled “Get a bucket”GET /v1/object-storage/buckets/{bucket} · scope objectstorage:read
curl https://api.truo.cloud/v1/object-storage/buckets/backups \ -H "Authorization: Bearer $TRUO_TOKEN"truo object-storage bucket get backupsawait truo.objectstorage.buckets.get("backups");truo_objectstorage({ "action": "bucket_get", "bucket": "backups"})operationId: objectstorage.buckets.get
List buckets
Section titled “List buckets”GET /v1/object-storage/buckets · scope objectstorage:read
Includes buckets created directly through the S3 protocol, which have no registry row: they are listed anyway —hiding them would hide data that exists— with created_at set to null and private access.
curl https://api.truo.cloud/v1/object-storage/buckets \ -H "Authorization: Bearer $TRUO_TOKEN"truo object-storage bucket listawait truo.objectstorage.buckets.list();truo_objectstorage({ "action": "bucket_list"})operationId: objectstorage.buckets.list
Get a bucket’s metrics
Section titled “Get a bucket’s metrics”GET /v1/object-storage/buckets/{bucket}/metrics · scope objectstorage:read
Storage, egress, and requests for the requested range. The series carry one point per UTC day and come back empty until there is data, rather than being padded with zeros that would be indistinguishable from a day without traffic.
curl https://api.truo.cloud/v1/object-storage/buckets/backups/metrics \ -H "Authorization: Bearer $TRUO_TOKEN"truo object-storage bucket metrics backupsawait truo.objectstorage.buckets.metrics("backups");truo_objectstorage({ "action": "bucket_metrics", "bucket": "backups"})operationId: objectstorage.buckets.metrics
Change a bucket’s visibility
Section titled “Change a bucket’s visibility”PATCH /v1/object-storage/buckets/{bucket} · scope objectstorage:write · reversible · idempotent
Making the bucket public mints an anonymous read URL (public_url) and keeps it if the bucket later goes private: republishing returns the same URL, not a new one.
curl https://api.truo.cloud/v1/object-storage/buckets/backups \ -X PATCH \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"access":"private"}'truo object-storage bucket update backupsawait truo.objectstorage.buckets.update("backups", {"access":"private"});truo_objectstorage({ "action": "bucket_update", "bucket": "backups"})operationId: objectstorage.buckets.update
Issue an access key
Section titled “Issue an access key”POST /v1/object-storage/keys · scope objectstorage:keys · reversible · idempotent
The only endpoint that returns secret_access_key, and it returns it exactly once: it is not stored in plaintext on our side and there is no way to recover it later. If it is lost, the way out is to delete the key and issue another. Keys coexist: issuing one does not revoke the previous ones. Limit each one to a bucket with scope so that losing one does not compromise the rest.
curl https://api.truo.cloud/v1/object-storage/keys \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"production-backups"}'truo object-storage key create <name>await truo.objectstorage.keys.create({"name":"production-backups"});truo_objectstorage({ "action": "key_create"})operationId: objectstorage.keys.create
Revoke an access key
Section titled “Revoke an access key”DELETE /v1/object-storage/keys/{key_id} · scope objectstorage:keys · destructive — cannot be undone · idempotent
Revocation is immediate. Revoking a key also invalidates the presigned URLs signed with it, even if they have not expired: the signature is validated against the key, and a revoked key no longer exists. It is the only way to cut off a presigned URL early.
curl https://api.truo.cloud/v1/object-storage/keys/AKIA7QF2K3M9XZ4NPRTV \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo object-storage key delete AKIA7QF2K3M9XZ4NPRTVawait truo.objectstorage.keys.delete("AKIA7QF2K3M9XZ4NPRTV");truo_objectstorage({ "action": "key_delete", "keyId": "AKIA7QF2K3M9XZ4NPRTV"})operationId: objectstorage.keys.delete
List access keys
Section titled “List access keys”GET /v1/object-storage/keys · scope objectstorage:read
Active keys only, and never the secret.
curl https://api.truo.cloud/v1/object-storage/keys \ -H "Authorization: Bearer $TRUO_TOKEN"truo object-storage key listawait truo.objectstorage.keys.list();truo_objectstorage({ "action": "key_list"})operationId: objectstorage.keys.list
Delete objects
Section titled “Delete objects”POST /v1/object-storage/buckets/{bucket}/objects/delete · scope objectstorage:write · destructive — cannot be undone · idempotent
Batch delete by key. It is a POST rather than a DELETE because the key list goes in the body: not every HTTP client sends a DELETE with a body. It cannot be undone. deleted can be lower than the number of keys requested: keys that did not exist do not count.
curl https://api.truo.cloud/v1/object-storage/buckets/backups/objects/delete \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"keys":["photos/logo.png"]}'truo object-storage object delete backupsawait truo.objectstorage.objects.delete("backups", {"keys":["photos/logo.png"]});truo_objectstorage({ "action": "object_delete", "bucket": "backups"})operationId: objectstorage.objects.delete
List a bucket’s objects
Section titled “List a bucket’s objects”GET /v1/object-storage/buckets/{bucket}/objects · scope objectstorage:read
One level at a time, like a file explorer: entries with is_folder: true are prefixes, navigated by passing their key as prefix. It does not accept limit: the backend sets the page size (up to 1000 entries) and trimming here would silently drop objects as the cursor advances.
curl https://api.truo.cloud/v1/object-storage/buckets/backups/objects \ -H "Authorization: Bearer $TRUO_TOKEN"truo object-storage object list backupsawait truo.objectstorage.objects.list("backups");truo_objectstorage({ "action": "object_list", "bucket": "backups"})operationId: objectstorage.objects.list
Presign a temporary URL
Section titled “Presign a temporary URL”POST /v1/object-storage/buckets/{bucket}/presign · scope objectstorage:read · reversible
Returns a link that works without credentials until it expires. method: "GET" to download (requires objectstorage:read), method: "PUT" to upload (requires objectstorage:write). The URL is a bearer credential: it works for anyone who holds it, and the only way to cut it off before it expires is to revoke the S3 key that signed it. Request the shortest TTL that works for you. It also inherits the scope of that key: if the key is limited to one bucket or is read-only, the URL can do no more than the key can.
curl https://api.truo.cloud/v1/object-storage/buckets/backups/presign \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"key":"photos/logo.png"}'truo object-storage object presign backups <key>await truo.objectstorage.objects.presign("backups", {"key":"photos/logo.png"});truo_objectstorage({ "action": "object_presign", "bucket": "backups"})operationId: objectstorage.objects.presign
Get the account’s Object Storage
Section titled “Get the account’s Object Storage”GET /v1/object-storage · scope objectstorage:read
Usage, endpoint, and status. It is a per-account singleton: there is no listing and no id to pass. Storage and object counts come from the latest daily snapshot, not a live scan, so a freshly uploaded object can take a while to show up in the totals.
curl https://api.truo.cloud/v1/object-storage \ -H "Authorization: Bearer $TRUO_TOKEN"truo object-storage getawait truo.objectstorage.tenant.get();truo_objectstorage({ "action": "get"})operationId: objectstorage.tenant.get