Capability group 03

Object storage (S3/R2)

RustFS is productized as public object storage on the same claim plane: buckets with versioning and lifecycle, objects with prefix/delimiter listing, multipart uploads, scoped S3 access keys and presigned GET/PUT — under /api/v1/buckets and /api/v1/object. A native S3 wire gateway at /s3/… speaks AWS Signature V4, so aws-cli, boto3 and any S3 SDK connect directly, with the tenant resolved from the access key (no X-Tenant-Id header).

RustFS · buckets · SigV4 wire gateway

SigV4
real aws-cli / boto3 wire protocol
$0
intra-edge egress (zero-rated)
path-style
S3 XML: CreateBucket, ListObjectsV2, multipart

Sub-capabilities

Public S3/R2 object storage with a real AWS SigV4 wire gateway.

Buckets, versioning & lifecycle

Create buckets (each backed by a RustFS claim) with versioning and visibility; set per-bucket lifecycle rules that expire old versions or abort stale multipart uploads. List, get and force-delete buckets.

Objects, versions & multipart

Put/get/head/delete object metadata with prefix + delimiter folder rollup and pagination; list all versions incl. delete markers; initiate multipart uploads, upload parts (etag per part), and complete with strictly ascending parts.

Scoped S3 access keys

Issue an S3 access key (the R2 API token) — the secret is shown once — list keys (never the secret), and revoke by id. The gateway resolves the tenant from the key, so nothing trusts a header.

Presigned URLs, HMAC-signed

Presign a GET or PUT for a key (HMAC over a canonical string, TTL-bounded), and verify a presigned request server-side — valid within TTL, signature + method must match.

AWS SigV4 wire gateway

The /s3 endpoint reconstructs the SigV4 canonical request and resolves the tenant from the access-key-id — no header trust. A bad signature returns a real <Error>SignatureDoesNotMatch</Error> envelope with the S3 status.

Zero-egress-to-edge accounting

Bytes served from a bucket to the SS-27 CDN are classified intra-edge and zero-rated in the usage ledger; external egress is billable. Per-bucket and tenant-wide usage summaries separate the two.

Endpoints

18 real operations on the /api/v1 contract.

Method + path straight from the SS-02 OpenAPI spec. Governed routes take an SS-01 bearer token and, where marked, an X-Tenant-Id; the S3 wire gateway authenticates with AWS SigV4 instead.

POST/api/v1/bucketsbearertenant

Create a bucket (backed by a RustFS claim): versioning, visibility, region.

GET/api/v1/bucketsbearertenant

List the tenant's buckets.

PUT/api/v1/buckets/{bucket}/versioningbearertenant

Enable or suspend bucket versioning.

PUT/api/v1/buckets/{bucket}/lifecyclebearertenant

Set per-bucket lifecycle rules (expire versions, abort stale uploads).

GET/api/v1/buckets/{bucket}/objectsbearertenant

List objects with prefix + delimiter folder rollup and pagination.

PUT/api/v1/buckets/{bucket}/objects/{key}bearertenant

Put an object (control-plane metadata: size, content-type, user metadata).

GET/api/v1/buckets/{bucket}/versionsbearertenant

List object versions (all versions incl. delete markers).

POST/api/v1/buckets/{bucket}/uploadsbearertenant

Initiate a multipart upload; parts via PUT …/uploads/{upload}/parts/{part}.

POST/api/v1/buckets/{bucket}/uploads/{upload}/completebearertenant

Complete a multipart upload (parts present + strictly ascending).

POST/api/v1/buckets/{bucket}/presignbearertenant

Presign a GET or PUT for a key (HMAC over a canonical string, TTL-bounded).

POST/api/v1/object/access-keysbearertenant

Issue an S3 access key (the R2 API token). Secret shown once.

GET/api/v1/object/access-keysbearertenant

List access keys (never returns the secret).

POST/api/v1/object/presign/verifybearertenant

Verify a presigned request (valid within TTL, signature + method match).

GET/api/v1/object/usagebearertenant

Tenant-wide usage summary (aggregate across buckets, zero-rated vs billable egress).

PUT/s3/{bucket}aws sigv4

S3 CreateBucket — SigV4, tenant from the access key.

GET/s3/{bucket}aws sigv4

S3 ListObjectsV2 (GET Bucket) — real S3 XML.

PUT/s3/{bucket}/{key}aws sigv4

S3 PutObject / UploadPart.

GET/s3/{bucket}/{key}aws sigv4

S3 GetObject.

Worked example

Presign an object, then connect boto3 to the SigV4 gateway.

Presign an object, then connect boto3 to the SigV4 gateway · python
# --- Native API: presign a PUT (HMAC, TTL-bounded) ---------------
# POST $BASE/buckets/site-assets/presign
#   {"method":"PUT","key":"app.v42.js","ttl_seconds":900}
# → {"url":"https://s3.dbaas.nexocloud.io/s3/site-assets/app.v42.js?...",
#    "expires":"2026-07-05T12:15:00Z"}

# --- S3 wire gateway: boto3 talks straight to /s3 ---------------
import boto3
s3 = boto3.client(
    "s3",
    endpoint_url="https://s3.dbaas.nexocloud.io/s3",
    aws_access_key_id=ACCESS_KEY,      # POST /api/v1/object/access-keys
    aws_secret_access_key=SECRET_KEY,  # shown once, at creation
    region_name="nexo-1",
)
s3.create_bucket(Bucket="site-assets")            # → PUT /s3/site-assets
s3.upload_file("app.v42.js", "site-assets", "app.v42.js")
for o in s3.list_objects_v2(Bucket="site-assets")["Contents"]:
    print(o["Key"], o["Size"])
# The gateway reconstructs the SigV4 canonical request and resolves
# the tenant from the access-key-id — there is NO X-Tenant-Id header.

aws-cli works identically: aws --endpoint-url https://s3.dbaas.nexocloud.io/s3 s3 mb s3://site-assets. A bad signature returns a real <Error>SignatureDoesNotMatch</Error> S3 envelope.

In the architecture

Where it sits on the data plane.

It is the same SS-02 object engine the Edge Cloud portal fronts as R2 — one engine, two product lenses. Buckets are RustFS claims, so they inherit per-tenant KEK encryption, backup and metering. The /s3 gateway is a thin SigV4 front over the same object model; egress to the SS-27 edge is classified intra-edge and zero-rated in the SS-60 usage ledger.

ADR-0073Sovereign edge cloud (S3/R2 lens)ADR-0056DBaaS is the only data plane