Skip to content

S3-Compatible API

AWS compatible

Quake AI Object Storage exposes an S3-compatible gateway. Use standard AWS SDKs, the AWS CLI, boto3, s3cmd, rclone, or any S3-compatible tool. Point it at the Quake AI endpoint.

Looking for the native OpenStack API? Swift API reference →

Authentication

The S3 API uses EC2 credentials (access key + secret key), not Keystone tokens. These are the same credential type used by AWS, so your tools don't need to know about OpenStack.

Create credentials

Console: Go to Storage → Object Storage → S3 Credentials and select Create S3 Credential. Copy the access key and secret key immediately: the secret is shown only once.

CLI:

openstack ec2 credentials create

# Output:
# +------------+--------------------------------------+
# | Field      | Value                                |
# +------------+--------------------------------------+
# | access     | fgmWGy9r92iv5RRk0kscAWSWAs2Pu00O     |
# | secret     | tYuXhyDFE2v888k43sRIn0zs2Pu0RRkt     |
# +------------+--------------------------------------+

# List existing credentials
openstack ec2 credentials list

Credentials are tied to your user account and work across all Quake AI Cloud projects. See How to create S3 credentials for the full walkthrough.

Endpoints

Each region has its own S3 endpoint. Use path-style addressing; virtual-hosted-style is not supported.

RegionEndpoint
US East 1https://object.us-east-1.rumble.cloud
US East 2https://object.us-east-2.rumble.cloud
US West 1https://object.us-west-1.rumble.cloud

Replace the endpoint in all SDK configuration examples below with the one matching your region.

SDK examples

Copy-paste ready examples for the most popular S3 tools. Replace YOUR_ACCESS_KEY and YOUR_SECRET_KEY with your EC2 credentials.

Python (boto3)

import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="https://object.us-east-2.rumble.cloud",
    aws_access_key_id="YOUR_ACCESS_KEY",
    aws_secret_access_key="YOUR_SECRET_KEY",
)

# List buckets
for bucket in s3.list_buckets()["Buckets"]:
    print(bucket["Name"])

# Upload a file
s3.upload_file("local-file.txt", "my-bucket", "remote-file.txt")

# Download a file
s3.download_file("my-bucket", "remote-file.txt", "downloaded.txt")

# Generate a presigned URL (valid 1 hour)
url = s3.generate_presigned_url(
    "get_object",
    Params={"Bucket": "my-bucket", "Key": "remote-file.txt"},
    ExpiresIn=3600,
)
print(url)

AWS CLI

# Configure a named profile
aws configure set aws_access_key_id YOUR_ACCESS_KEY --profile rumble
aws configure set aws_secret_access_key YOUR_SECRET_KEY --profile rumble
aws configure set region us-east-1 --profile rumble
aws configure set endpoint_url https://object.us-east-1.rumble.cloud --profile rumble

# List buckets
aws s3 ls --profile rumble

# Create a bucket
aws s3 mb s3://my-bucket --profile rumble

# Upload a file
aws s3 cp local-file.txt s3://my-bucket/ --profile rumble

# Sync a directory
aws s3 sync ./local-dir s3://my-bucket/prefix/ --profile rumble

# Delete a bucket and all contents
aws s3 rb s3://my-bucket --force --profile rumble

s3cmd

# Configure s3cmd (use the downloaded .cfg or configure manually)
# ~/.s3cfg or pass -c /path/to/config.cfg

[default]
access_key = YOUR_ACCESS_KEY
secret_key = YOUR_SECRET_KEY
host_base = object.us-east-2.rumble.cloud
host_bucket = object.us-east-2.rumble.cloud
use_https = True

# List buckets
s3cmd ls

# Create a bucket
s3cmd mb s3://my-bucket

# Upload a file
s3cmd put local-file.txt s3://my-bucket/

# Download a file
s3cmd get s3://my-bucket/remote-file.txt ./downloaded.txt

# Set CORS (XML only)
s3cmd setcors cors-policy.xml s3://my-bucket

Go (aws-sdk-go-v2)

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/credentials"
	"github.com/aws/aws-sdk-go-v2/service/s3"
)

func main() {
	cfg, err := config.LoadDefaultConfig(context.TODO(),
		config.WithRegion("us-east-1"),
		config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
			"YOUR_ACCESS_KEY",
			"YOUR_SECRET_KEY",
			"",
		)),
	)
	if err != nil {
		log.Fatal(err)
	}

	client := s3.NewFromConfig(cfg, func(o *s3.Options) {
		o.BaseEndpoint = aws.String("https://object.us-east-1.rumble.cloud")
		o.UsePathStyle = true
	})

	out, err := client.ListBuckets(context.TODO(), &s3.ListBucketsInput{})
	if err != nil {
		log.Fatal(err)
	}
	for _, b := range out.Buckets {
		fmt.Println(*b.Name)
	}
}

UsePathStyle: true is required; virtual-hosted-style bucket addressing is not supported.

Operation compatibility

Supported operations map to the AWS S3 API. Unsupported operations return 501 Not Implemented or 403 Forbidden. Partial support means the operation works with limitations noted below.

✓ Supported~ Partial✗ Unsupported

Buckets

✓ListBuckets
✓CreateBucket
✓DeleteBucket
✓HeadBucket
✓GetBucketLocation
✓GetBucketAcl
✓PutBucketAcl

Objects

✓ListObjectsV2
✓ListObjects (v1)
✓PutObject
✓GetObject
✓HeadObject
✓DeleteObject
✓DeleteObjects (multi)
✓CopyObject
✓PutObjectAcl
✓GetObjectAcl

Multipart

✓CreateMultipartUpload
✓UploadPart
✓CompleteMultipartUpload
✓AbortMultipartUpload
✓ListMultipartUploads
✓ListParts

CORS

✓PutBucketCors
✓GetBucketCors
✓DeleteBucketCors

Versioning

~PutBucketVersioning
✓GetBucketVersioning
~ListObjectVersions

Lifecycle

✗PutBucketLifecycle

Events

✗PutBucketNotification

Policies

✗PutBucketPolicy
✗GetBucketPolicy

Tagging

✗PutBucketTagging

Query

✗SelectObjectContent

Encryption

✗PutBucketEncryption

Limitations vs. AWS S3

The S3-compatible gateway is backed by OpenStack Swift. Some AWS S3 features have no Swift equivalent and are not available.

Lifecycle policies

PutBucketLifecycleConfiguration is not implemented. Object expiration must be managed via the Swift native API or application logic.

Event notifications

No SNS/SQS/Lambda-style event notifications. Poll for changes or use application-level event tracking.

IAM / Bucket policies

PutBucketPolicy and IAM-style policies are not supported. Access control uses Swift ACLs via the native API.

Versioning

Basic enable/suspend and delete markers work. MFA delete and advanced versioning features are not available.

Server-side encryption

PutBucketEncryption is not implemented via S3. Use Swift X-Object-Meta-Encryption headers via the native API.

Object tagging

PutObjectTagging and PutBucketTagging are not supported. Use Swift metadata headers instead.

Select / query

SelectObjectContent (S3 Select) is not available. Download objects and process locally.

Virtual-hosted-style addressing

Only path-style addressing is supported. Set UsePathStyle/force_path_style in your SDK configuration.

Bucket naming

Bucket names are scoped to your project (tenant), not globally unique. URLs include the tenant ID prefix for unauthenticated access.

CORS configuration

Enable cross-origin access for web applications by setting a CORS policy on your bucket. Both JSON (AWS CLI) and XML (s3cmd) formats work.

# Set CORS with AWS CLI (JSON)
aws s3api put-bucket-cors --profile rumble \
  --bucket my-bucket \
  --cors-configuration '{
    "CORSRules": [{
      "AllowedOrigins": ["https://app.example.com"],
      "AllowedMethods": ["GET", "PUT", "POST"],
      "AllowedHeaders": ["*"],
      "MaxAgeSeconds": 3600
    }]
  }'

# Set CORS with s3cmd (XML file)
s3cmd setcors cors-policy.xml s3://my-bucket

# Verify
aws s3api get-bucket-cors --profile rumble --bucket my-bucket
Tenant ID in URLs: CORS preflight requests are unauthenticated. For direct browser access, include the tenant ID in the object URL: https://object.{region}.rumble.cloud/{tenant_id}:bucket/object. See the full CORS guide for details and common pitfalls.

Large object support

Objects larger than 5 GB must use multipart upload. The S3 multipart API is fully supported.

LimitValue
Max single PUT size5 GB
Max object size (multipart)5 TB
Part size range5 MB – 5 GB
Max parts per upload10,000
Max concurrent multipart uploadsNo hard limit (project quota applies)

Both the AWS CLI and boto3 handle multipart uploads automatically for large files. No extra configuration needed.

Related