13. S3 Gateway
VDURA’s S3 gateway exposes tenant volumes over the S3 protocol. Configuration is split across five areas: per-tenant gateway config, realm-wide S3 administration (Platform Admin only), buckets, credential lookup, and the gateway instances themselves.
13.1 Tenant S3 Configuration
Endpoint Summary
| Path | Summary | Method |
|---|---|---|
/api/v1/tenants/{tenant_id}/s3 |
Get S3 config | GET |
/api/v1/tenants/{tenant_id}/s3 |
Create S3 config | PUT |
/api/v1/tenants/{tenant_id}/s3 |
Update S3 config | PATCH |
/api/v1/tenants/{tenant_id}/s3 |
Delete S3 config | DELETE |
/api/v1/tenants/{tenant_id}/s3/tls/renew |
Renew the realm-generated S3 edge cert | POST |
GET /api/v1/tenants/{tenant_id}/s3
Get S3 config
Returns the S3 gateway configuration for the specified tenant.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Successfully retrieved the S3 configuration for the tenant. | GetS3ConfigResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | S3 configuration not found for this tenant. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (200, GetS3ConfigResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
s3_config |
S3ConfigSchema object |
yes | n/a | Tenant S3 gateway configuration. |
Example
{
"s3_config" : {
"default_dir_mode" : "755",
"default_group" : "gid:65534",
"default_obj_mode" : "644",
"default_owner" : "uid:65534",
"default_prefix" : "/vol1/t1",
"enabled" : true,
"tls" : {}
}
}PUT /api/v1/tenants/{tenant_id}/s3
Create S3 config
Creates the S3 gateway configuration for the specified tenant. 409 if a config already exists; callers needing replace semantics must DELETE first.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Request Body (application/json, required)
Schema: S3ConfigCreateRequest
Request payload for creating a tenant’s S3 configuration.
Every field carries a default, so PUT /s3 {} is a complete request: it creates the disabled config with the empty prefix and the POSIX defaults a tenant starts with. Those defaults live here rather than in a factory beside the service, because this is the only place that can publish them - an operator reading the API document sees what an omitted field will be, and there is no second copy to drift from it.
access_key, secret_key and control_plane have no field here at all; :mod:app.use_cases.create_s3_config mints all three.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
access_log |
boolean | no | true |
Whether the S3 access log is enabled for this tenant (the S3 gateway’s –access-log audit log). Defaults to true. | |
aggregator |
S3AggregatorConfigSchema object |
no | n/a | Tenant-level S3 aggregator policy (opt-in via enabled=true). |
|
debug_log |
boolean | no | false |
Whether the S3 debug request log is enabled for this tenant. Defaults to false. | |
default_dir_mode |
string | no | 755 |
755, 0755 |
Default directory permission mode in octal (e.g. 755). |
default_group |
string | no | gid:65534 |
gid:65534, gid:1000 |
Default POSIX group for objects (e.g. gid:65534). An fs-user created without a gid takes it. gid:65534 (nogroup) is the unset value; an empty string is not accepted. |
default_obj_mode |
string | no | 644 |
644, 0644 |
Default object/file permission mode in octal (e.g. 644). |
default_owner |
string | no | uid:65534 |
uid:65534, uid:1000 |
Default POSIX owner for objects (e.g. uid:65534). An fs-user created without a uid takes it. uid:65534 (nobody) is the unset value; an empty string is not accepted. |
default_prefix |
string | no | (empty string) | /vol1, /vol1/data |
Tenant-relative path served by S3 (e.g. /vol1 or /vol1/data). Must name an existing volume of the tenant (or a directory inside one); stored resolved into the tenant volume namespace. Optional: omit it to create the config before the tenant has a volume to serve. The empty value skips volume validation and cannot be enabled until a real prefix is set by PATCH. |
dns |
S3DnsConfigSchema object |
no | n/a | DNS/named-access policy (default mode=off). | |
enabled |
boolean | no | false |
Whether S3 access is enabled for this tenant. | |
metrics_enabled |
boolean | no | true |
Whether S3 metrics are collected for this tenant (the S3 gateway’s StatsD counters to the node-local Telegraf listener). Defaults to true. | |
tls |
S3TlsConfigSchema object |
no | n/a | TLS configuration for the S3 gateway. |
Example
{
"aggregator" : {
"enabled" : false
},
"default_dir_mode" : "755",
"default_group" : "gid:65534",
"default_obj_mode" : "644",
"default_owner" : "uid:65534",
"default_prefix" : "/vol1/t1",
"enabled" : true,
"tls" : {}
}Responses
| Status | Description | Schema |
|---|---|---|
| 201 | S3 configuration created successfully. | - |
| 400 | Bad request: the S3 configuration is invalid for the requested state - enabling without a default_prefix, TLS material that fails validation for the resulting tls.mode, or a tenant short name and realm DNS zone too long to name a realm-generated edge cert. |
- |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 409 | S3 configuration already exists for this tenant. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
| 503 | The realm key service could not be reached to sign the S3 edge cert; retry. | - |
PATCH /api/v1/tenants/{tenant_id}/s3
Update S3 config
Partially updates the S3 gateway configuration for the specified tenant. The response names the fields of this request that already-running gateway instances will not pick up until they are restarted - empty when every change is in effect now, which includes a tenant with no started instance.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Request Body (application/json, required)
Schema: S3ConfigPatchRequest
Request payload for partially updating a tenant’s S3 configuration.
At least one field must be provided. Null values are not allowed - omit fields that should not change.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
access_log |
boolean, nullable | no | null |
Whether the S3 access log is enabled for this tenant. | |
aggregator |
S3AggregatorConfigSchema object, nullable |
no | null |
Tenant-level S3 aggregator policy (opt-in via enabled=true). |
|
debug_log |
boolean, nullable | no | null |
Whether the S3 debug request log is enabled for this tenant. | |
default_dir_mode |
string, nullable | no | null |
Default directory permission mode in octal. | |
default_group |
string, nullable | no | null |
Default POSIX group for objects. To unset it, send gid:65534 (nogroup), the value a new config starts with; an empty string is not accepted. | |
default_obj_mode |
string, nullable | no | null |
Default object/file permission mode in octal. | |
default_owner |
string, nullable | no | null |
Default POSIX owner for objects. To unset it, send uid:65534 (nobody), the value a new config starts with; an empty string is not accepted. | |
default_prefix |
string, nullable | no | null |
Tenant-relative path served by S3. Must name an existing volume of the tenant (or a directory inside one); immutable while the tenant has S3 enabled. | |
dns |
S3DnsConfigSchema object, nullable |
no | null |
DNS/named-access policy (mode off/panfs/external). | |
enabled |
boolean, nullable | no | null |
Whether S3 access is enabled for this tenant. | |
metrics_enabled |
boolean, nullable | no | null |
Whether S3 metrics are collected for this tenant. | |
tls |
S3TlsConfigSchema object, nullable |
no | null |
TLS configuration for the S3 gateway. |
Example
{
"default_prefix" : "/vol1/t1",
"enabled" : false
}Responses
| Status | Description | Schema |
|---|---|---|
| 200 | S3 configuration updated successfully. The body names the fields of this request that already-running gateway instances will not pick up until they are restarted; it is empty when every change is in effect now. | S3ConfigUpdateResponse |
| 400 | Bad request: the S3 configuration is invalid for the requested state - enabling without a default_prefix, TLS material that fails validation for the resulting tls.mode, or a tenant short name and realm DNS zone too long to name a realm-generated edge cert. |
- |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | S3 configuration not found for this tenant. | - |
| 409 | The request conflicts with the tenant S3 state: disabling while an instance is desired started, changing default_prefix while enabled, or deleting the config while any instance record exists. |
- |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
| 503 | The realm key service could not be reached to sign the S3 edge cert; retry. | - |
Response Body (200, S3ConfigUpdateResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
restart_required |
array of string | yes | n/a | Names of S3 config fields this request changed whose new value reaches already-running S3 gateway instances only after those instances are restarted. Empty when every change is in effect now, when the request changed nothing, or when the tenant has no started instance. The set of names can change between releases. |
Example
{
"restart_required" : [
"access_log",
"debug_log"
]
}DELETE /api/v1/tenants/{tenant_id}/s3
Delete S3 config
Deletes the S3 gateway configuration for the specified tenant.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Responses
| Status | Description | Schema |
|---|---|---|
| 204 | S3 configuration deleted successfully. No content in response body. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | S3 configuration not found for this tenant. | - |
| 409 | The request conflicts with the tenant S3 state: disabling while an instance is desired started, changing default_prefix while enabled, or deleting the config while any instance record exists. |
- |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
POST /api/v1/tenants/{tenant_id}/s3/tls/renew
Renew the realm-generated S3 edge cert
Forces a fresh mint of the tenant S3 edge certificate and stores it with the tenant’s S3 configuration. Manual operator action: renewal is not automated, so an operator runs this before the stored cert lapses. Valid only when tls.mode = realm-generated (the realm owns that cert); a tenant-provided cert is renewed by uploading a new one via PATCH /s3 and this returns 409. The minted SANs follow the tenant’s current aggregator.enabled state. Renewal disrupts nothing: a running gateway re-reads the cert and key without restarting. Authorization: gated on s3-config:update, like every other write to the tenant S3 config; the tenant admin and platform-admin both hold it.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Responses
| Status | Description | Schema |
|---|---|---|
| 204 | S3 edge cert renewed and stored. No content in response body. | - |
| 400 | The tenant short name and realm DNS zone are too long to name the edge cert. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | S3 configuration not found for this tenant. | - |
| 409 | Tenant uses tls.mode=tenant-provided; upload a new cert instead. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
| 503 | The realm key service could not be reached to sign the S3 edge cert; retry. | - |
13.2 Realm-Wide S3 Administration
Endpoint Summary
| Path | Summary | Method |
|---|---|---|
/api/v1/s3/enabled |
Get realm-wide S3 enable flag | GET |
/api/v1/s3/enabled |
Set realm-wide S3 enable flag | PUT |
/api/v1/s3/prefix |
Get realm-wide S3 prefix path | GET |
/api/v1/s3/prefix |
Set realm-wide S3 prefix path | PUT |
GET /api/v1/s3/enabled
Get realm-wide S3 enable flag
Returns the realm-wide S3 enable flag. Distinct from the per-tenant s3 enabled toggle on /api/v1/tenants/{id}/s3/; this flag gates provisioning of any new tenant-level S3 state. A fresh realm reports enabled=false until the operator opts in via PUT.
Authentication: required (PasswordLogin or BearerJWT).
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Current realm-wide enable flag. | S3EnabledResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 500 | Internal server error. | - |
Response Body (200, S3EnabledResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
enabled |
boolean | yes | n/a | Realm-wide S3 enable flag. | |
tenants_with_s3_enabled |
array of string, nullable | no | null |
Tenant short names that still have tenant-level S3 enabled after the realm-wide disable. Absent on get and on enable. | |
warning |
string, nullable | no | null |
Warning surfaced when realm-disable leaves tenants with S3 enabled. |
Example
{
"enabled" : false,
"tenants_with_s3_enabled" : [
"tn1"
],
"warning" : "1 tenant(s) still have S3 enabled at the tenant level"
}PUT /api/v1/s3/enabled
Set realm-wide S3 enable flag
Sets the realm-wide S3 enable flag. A successful write takes effect on every node: turning S3 off stops every running S3 gateway instance; turning it back on re-applies per-tenant intent and brings the gateways back up. Per-tenant enabled records are preserved across both transitions. Disabling while at least one tenant has tenant-level S3 enabled is permitted but surfaces a warning listing the still-enabled tenants in the response.
Authentication: required (PasswordLogin or BearerJWT).
Request Body (application/json, required)
Schema: S3EnabledRequest
Request body for PUT /s3/enabled.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
enabled |
boolean | yes | n/a | Realm-wide S3 enable flag. |
Example
{
"enabled" : true
}Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Realm-wide enable flag updated. | S3EnabledResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (200, S3EnabledResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
enabled |
boolean | yes | n/a | Realm-wide S3 enable flag. | |
tenants_with_s3_enabled |
array of string, nullable | no | null |
Tenant short names that still have tenant-level S3 enabled after the realm-wide disable. Absent on get and on enable. | |
warning |
string, nullable | no | null |
Warning surfaced when realm-disable leaves tenants with S3 enabled. |
Example
{
"enabled" : false,
"tenants_with_s3_enabled" : [
"tn1"
],
"warning" : "1 tenant(s) still have S3 enabled at the tenant level"
}GET /api/v1/s3/prefix
Get realm-wide S3 prefix path
Returns the realm-wide S3 prefix path that anchors per-tenant prefixes under a common root. Distinct from the per-tenant default_prefix carried on /api/v1/tenants/{id}/s3/. A fresh realm reports the empty string until the operator sets a value.
Authentication: required (PasswordLogin or BearerJWT).
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Current realm-wide prefix. | S3PrefixResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 500 | Internal server error. | - |
Response Body (200, S3PrefixResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
prefix |
string | yes | n/a | Current realm-wide path prefix. |
Example
{
"prefix" : "/realm-root"
}PUT /api/v1/s3/prefix
Set realm-wide S3 prefix path
Sets the realm-wide S3 prefix path. Non-empty values MUST begin with “/” and MUST NOT carry trailing whitespace. An empty string clears the prefix.
Authentication: required (PasswordLogin or BearerJWT).
Request Body (application/json, required)
Schema: S3PrefixRequest
Request body for PUT /s3/prefix.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
prefix |
string | yes | n/a | Realm-wide POSIX-style path prefix. Empty string means “no realm-wide prefix”; otherwise the value MUST begin with “/” and MUST NOT contain trailing whitespace. |
Example
{
"prefix" : "/realm-root"
}Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Realm-wide prefix updated. | S3PrefixResponse |
| 400 | Invalid prefix (missing leading “/” or trailing whitespace). | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (200, S3PrefixResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
prefix |
string | yes | n/a | Current realm-wide path prefix. |
Example
{
"prefix" : "/realm-root"
}13.3 S3 Buckets
Endpoint Summary
| Path | Summary | Method |
|---|---|---|
/api/v1/tenants/{tenant_id}/s3/buckets |
List S3 buckets | GET |
/api/v1/tenants/{tenant_id}/s3/buckets |
Create S3 bucket | POST |
/api/v1/tenants/{tenant_id}/s3/buckets/{name} |
Delete S3 bucket | DELETE |
GET /api/v1/tenants/{tenant_id}/s3/buckets
List S3 buckets
List the tenant’s S3 buckets, as a running gateway reports them.
The gateway is the only bucket catalogue, so a tenant with no running instance cannot be listed: that is a 503, never an empty list.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | The buckets a running S3 gateway holds for the tenant. | ListS3BucketsResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
| 503 | No running S3 instance could answer, and nothing was performed. The response detail names which case applies and the panctl command that resolves it. | - |
Response Body (200, ListS3BucketsResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
s3_buckets |
array of S3BucketSchema object |
yes | n/a | List of S3 buckets. |
POST /api/v1/tenants/{tenant_id}/s3/buckets
Create S3 bucket
Create an S3 bucket on a running gateway, owned by the PanFS user the request names.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Request Body (application/json, required)
Schema: S3BucketCreateRequest
Request payload for creating an S3 bucket.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
name |
string | yes | n/a | Bucket name. | |
owner |
string | yes | n/a | S3 access_key of the PanFS user the bucket belongs to. Required, and must name a PanFS user in this tenant: the gateway resolves the owner through its IAM backend, so a value that is not a PanFS user cannot be resolved there at all. |
Example
{
"name" : "my-bucket",
"owner" : "alice"
}Responses
| Status | Description | Schema |
|---|---|---|
| 201 | S3 bucket created on a running gateway. | - |
| 400 | The S3 gateway refused the operation - most often a bucket name outside the gateway-enforced AWS rules (3-63 characters, lowercase, DNS-shaped, not an IP address). | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 409 | S3 bucket already exists. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
| 503 | No running S3 instance could answer, and nothing was performed. The response detail names which case applies and the panctl command that resolves it. | - |
| 504 | No running S3 instance confirmed the create, and the bucket may exist. List the buckets to check. | - |
DELETE /api/v1/tenants/{tenant_id}/s3/buckets/{name}
Delete S3 bucket
Delete an S3 bucket from a running gateway.
A non-empty bucket returns 409 and stays listed. A bucket the gateway does not hold is a 404, unless an earlier attempt of this same request may have deleted it.
Authorization is the s3-buckets:delete verb plus caller-tenant scoping, like every other operation on this surface. Deletion is NOT conditioned on the caller having created the bucket.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
name |
path | string | yes | S3 bucket name |
Responses
| Status | Description | Schema |
|---|---|---|
| 204 | S3 bucket deleted from a running gateway. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found, or S3 bucket not found on the gateway. | - |
| 409 | S3 bucket still holds objects; empty it over the S3 API before deleting. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
| 503 | No running S3 instance could answer, and nothing was performed. The response detail names which case applies and the panctl command that resolves it. | - |
| 504 | No running S3 instance confirmed the delete, and the bucket may have been deleted. List the buckets to check. | - |
13.4 S3 Credentials
Endpoint Summary
| Path | Summary | Method |
|---|---|---|
/api/v1/tenants/{tenant_id}/s3-credentials/{access_key}/reveal |
Resolve an S3 access key to the identity behind it | POST |
POST /api/v1/tenants/{tenant_id}/s3-credentials/{access_key}/reveal
Resolve an S3 access key to the identity behind it
Return the credential behind an access key, with the profile and POSIX ids that go with it - everything an auth.Account needs, because the access key is the only handle the data plane holds and a second lookup would cost a round trip per IAM cache miss. Called by the S3 gateway on that miss, through POST /s3/gateway/s3-credentials/{access_key}/reveal, and gated on fs-users:read-s3-secret. A disabled user answers 404, which the gateway maps to its no-such-user error.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID). |
access_key |
path | string | yes | S3 access key |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Credential and identity returned. | S3CredentialsRevealResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | No PanFS user owns this access key in this tenant, or that user is disabled. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (200, S3CredentialsRevealResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
access_key |
string | yes | n/a | The access key that was resolved. | |
gid |
integer | yes | n/a | POSIX group id objects written by this user are owned by. | |
profile |
S3Profile (disabled, user, privileged) |
yes | n/a | S3 access profile. The gateway maps it to a role: privileged carries bucket-creation rights, anything else is an ordinary user. |
|
secret_key |
string | yes | n/a | Plaintext S3 secret key. | |
uid |
integer | yes | n/a | POSIX user id objects written by this user are owned by. |
Example
{
"access_key" : "alice",
"gid" : 65534,
"profile" : "user",
"secret_key" : "7f9a3d2e8c1b4a6f0d5e2c8b9f1a3d7e",
"uid" : 65534
}13.5 S3 Gateway Instances
Endpoint Summary
| Path | Summary | Method |
|---|---|---|
/api/v1/tenants/{tenant_id}/s3/instances |
List a tenant’s S3 gateway instances | GET |
/api/v1/tenants/{tenant_id}/s3/instances |
Create an S3 gateway instance | POST |
/api/v1/tenants/{tenant_id}/s3/instances/delete-all |
Delete every instance of the tenant and wait for teardown | POST |
/api/v1/tenants/{tenant_id}/s3/instances/start-all |
Start every instance of the tenant | POST |
/api/v1/tenants/{tenant_id}/s3/instances/stop-all |
Stop every instance of the tenant | POST |
/api/v1/tenants/{tenant_id}/s3/instances/{iid} |
Show an S3 gateway instance (bare-iid fan-out or specific node) | GET |
/api/v1/tenants/{tenant_id}/s3/instances/{iid} |
Delete an S3 gateway instance (fan-out if node omitted; requires confirm) | DELETE |
/api/v1/tenants/{tenant_id}/s3/instances/{iid}/start |
Start an S3 gateway instance (fan-out if node omitted) | POST |
/api/v1/tenants/{tenant_id}/s3/instances/{iid}/stop |
Stop an S3 gateway instance (fan-out if node omitted) | POST |
GET /api/v1/tenants/{tenant_id}/s3/instances
List a tenant’s S3 gateway instances
Return every instance of the tenant with its observed/health merged in.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | List of merged desired + observed instance views. | S3InstanceListResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (200, S3InstanceListResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
instances |
array of S3InstanceViewSchema object |
yes | n/a |
POST /api/v1/tenants/{tenant_id}/s3/instances
Create an S3 gateway instance
Writes the desired-state record for a new S3 gateway instance placed on the specified node. The instance starts in desired=created; use the start endpoint to bring it online. At most one instance per tenant per node, and the tenant must have a subnet: the gateway binds inside the tenant network namespace, which a tenant with no subnet does not have.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Request Body (application/json, required)
Schema: S3InstanceCreateRequest
Create a new S3 gateway instance for a tenant on a specific node.
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
dataserial |
string | yes | n/a | Target node’s dataserial (14 hex chars) as returned by GET /nodes and consumed by every /nodes/{dataserial} endpoint (e.g. 08823f71a1eecc). The stable placement identity - unaffected by node renames. |
|
instance_id |
string | yes | n/a | Instance identifier: exactly 5 alphanumeric characters (D10). |
Example
{
"dataserial" : "08823f71a1eecc",
"instance_id" : "abc12"
}Responses
| Status | Description | Schema |
|---|---|---|
| 201 | Instance created. | S3InstanceCreateResponse |
| 400 | Invalid iid format or unknown dataserial. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 409 | Instance already exists at (iid, dataserial), the tenant already has an instance on that node, or the tenant has no subnet. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (201, S3InstanceCreateResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
instance |
S3InstanceSchema object |
yes | n/a | Desired-state view of an S3 gateway instance. |
POST /api/v1/tenants/{tenant_id}/s3/instances/delete-all
Delete every instance of the tenant and wait for teardown
Fan out desired=deleting to every (iid, data_serial) owned by the tenant and block until the owning node has removed each instance record, which is the terminal state, or until timeout seconds elapse. Intended as the companion to the DELETE /api/v1/tenants/{id} guard: an operator runs delete-all first, waits for it to succeed, then deletes the tenant.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
timeout |
query | integer | no | Upper bound in seconds on the blocking wait for instance teardown. |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Every instance torn down; no rows remain. results lists what each pass recorded on each node (202 once recorded), so a node retried by a later pass appears more than once. | S3InstanceFanoutResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 409 | Timed out waiting for teardown; see error body. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (200, S3InstanceFanoutResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
aggregate_status |
integer | yes | n/a | Aggregate HTTP status: the rows’ shared success code (202 for a recorded state change, 200 for a read) if every node succeeded, the shared error code if every node failed with the same code, otherwise 207. delete-all answers 200 once every instance is gone, and start-all/stop-all answer 200 with no results for a tenant with no instances. | |
confirmed |
boolean | no | true |
For delete fan-out, true once the caller has confirmed the destructive operation. When false, the request body enumerates matches without deleting anything. | |
results |
array of S3InstancePerNodeResult object |
yes | n/a |
POST /api/v1/tenants/{tenant_id}/s3/instances/start-all
Start every instance of the tenant
Records the new desired state of every instance of the tenant and returns without waiting for them to reach it. Poll GET /tenants/{tenant_id}/s3/instances for the observed state.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | The tenant has no instances; nothing was recorded. | - |
| 202 | Recorded on every instance. | S3InstanceFanoutResponse |
| 207 | Partial success; see per-node results. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 409 | Every target refused: S3 is disabled for the tenant, the target’s node has its S3 service stopped, or the instance is being deleted (desired=deleting). | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (202, S3InstanceFanoutResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
aggregate_status |
integer | yes | n/a | Aggregate HTTP status: the rows’ shared success code (202 for a recorded state change, 200 for a read) if every node succeeded, the shared error code if every node failed with the same code, otherwise 207. delete-all answers 200 once every instance is gone, and start-all/stop-all answer 200 with no results for a tenant with no instances. | |
confirmed |
boolean | no | true |
For delete fan-out, true once the caller has confirmed the destructive operation. When false, the request body enumerates matches without deleting anything. | |
results |
array of S3InstancePerNodeResult object |
yes | n/a |
POST /api/v1/tenants/{tenant_id}/s3/instances/stop-all
Stop every instance of the tenant
Records the new desired state of every instance of the tenant and returns without waiting for them to reach it. Poll GET /tenants/{tenant_id}/s3/instances for the observed state.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | The tenant has no instances; nothing was recorded. | - |
| 202 | Recorded on every instance. | S3InstanceFanoutResponse |
| 207 | Partial success; see per-node results. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | Tenant not found. | - |
| 409 | Every instance is being deleted (desired=deleting) and cannot be stopped. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (202, S3InstanceFanoutResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
aggregate_status |
integer | yes | n/a | Aggregate HTTP status: the rows’ shared success code (202 for a recorded state change, 200 for a read) if every node succeeded, the shared error code if every node failed with the same code, otherwise 207. delete-all answers 200 once every instance is gone, and start-all/stop-all answer 200 with no results for a tenant with no instances. | |
confirmed |
boolean | no | true |
For delete fan-out, true once the caller has confirmed the destructive operation. When false, the request body enumerates matches without deleting anything. | |
results |
array of S3InstancePerNodeResult object |
yes | n/a |
GET /api/v1/tenants/{tenant_id}/s3/instances/{iid}
Show an S3 gateway instance (bare-iid fan-out or specific node)
Return one or many matching rows as a per-node result set.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
iid |
path | string | yes | Instance identifier: exactly 5 alphanumeric characters. |
dataserial |
query | string, nullable | no | Optional target node dataserial; without it, fan out over every matching (iid, data_serial). |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | One entry in results per matching node. | S3InstanceFanoutResponse |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | No matching instance for this iid under the tenant. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (200, S3InstanceFanoutResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
aggregate_status |
integer | yes | n/a | Aggregate HTTP status: the rows’ shared success code (202 for a recorded state change, 200 for a read) if every node succeeded, the shared error code if every node failed with the same code, otherwise 207. delete-all answers 200 once every instance is gone, and start-all/stop-all answer 200 with no results for a tenant with no instances. | |
confirmed |
boolean | no | true |
For delete fan-out, true once the caller has confirmed the destructive operation. When false, the request body enumerates matches without deleting anything. | |
results |
array of S3InstancePerNodeResult object |
yes | n/a |
DELETE /api/v1/tenants/{tenant_id}/s3/instances/{iid}
Delete an S3 gateway instance (fan-out if node omitted; requires confirm)
If dataserial is omitted and more than one node hosts a matching iid, confirm=true is required; otherwise the API returns 409 and a preview body listing the matches, so the caller can decide whether to proceed.
Records the delete and returns without waiting for the instance to be removed. Poll GET /tenants/{tenant_id}/s3/instances/{iid} until it answers 404: the instance is gone. If the delete named a dataserial, poll with the same dataserial: without it the GET still answers for the placements on other nodes.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
iid |
path | string | yes | Instance identifier: exactly 5 alphanumeric characters. |
dataserial |
query | string, nullable | no | Optional target node dataserial. |
confirm |
query | boolean | no | Required (true) when dataserial is omitted and multiple instances match - prevents destructive fan-out without explicit opt-in. |
Responses
| Status | Description | Schema |
|---|---|---|
| 202 | Recorded on every target. | S3InstanceFanoutResponse |
| 207 | Partial success; see per-node results. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | No matching instance. | - |
| 409 | Destructive fan-out without confirm=true; body lists matches. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (202, S3InstanceFanoutResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
aggregate_status |
integer | yes | n/a | Aggregate HTTP status: the rows’ shared success code (202 for a recorded state change, 200 for a read) if every node succeeded, the shared error code if every node failed with the same code, otherwise 207. delete-all answers 200 once every instance is gone, and start-all/stop-all answer 200 with no results for a tenant with no instances. | |
confirmed |
boolean | no | true |
For delete fan-out, true once the caller has confirmed the destructive operation. When false, the request body enumerates matches without deleting anything. | |
results |
array of S3InstancePerNodeResult object |
yes | n/a |
POST /api/v1/tenants/{tenant_id}/s3/instances/{iid}/start
Start an S3 gateway instance (fan-out if node omitted)
Records the new desired state and returns without waiting for the instance to reach it. Poll GET /tenants/{tenant_id}/s3/instances/{iid} for the observed state.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
iid |
path | string | yes | Instance identifier: exactly 5 alphanumeric characters. |
dataserial |
query | string, nullable | no | Optional target node dataserial. |
Responses
| Status | Description | Schema |
|---|---|---|
| 202 | Recorded on every target. | S3InstanceFanoutResponse |
| 207 | Mixed success; see per-node results. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | No matching instance. | - |
| 409 | Every target refused: S3 is disabled for the tenant, the target’s node has its S3 service stopped, or the instance is being deleted (desired=deleting). | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (202, S3InstanceFanoutResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
aggregate_status |
integer | yes | n/a | Aggregate HTTP status: the rows’ shared success code (202 for a recorded state change, 200 for a read) if every node succeeded, the shared error code if every node failed with the same code, otherwise 207. delete-all answers 200 once every instance is gone, and start-all/stop-all answer 200 with no results for a tenant with no instances. | |
confirmed |
boolean | no | true |
For delete fan-out, true once the caller has confirmed the destructive operation. When false, the request body enumerates matches without deleting anything. | |
results |
array of S3InstancePerNodeResult object |
yes | n/a |
POST /api/v1/tenants/{tenant_id}/s3/instances/{iid}/stop
Stop an S3 gateway instance (fan-out if node omitted)
Records the new desired state and returns without waiting for the instance to reach it. Poll GET /tenants/{tenant_id}/s3/instances/{iid} for the observed state.
Authentication: required (PasswordLogin or BearerJWT).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tenant_id |
path | string (uuid) | yes | Tenant identifier (UUID) |
iid |
path | string | yes | Instance identifier: exactly 5 alphanumeric characters. |
dataserial |
query | string, nullable | no | Optional target node dataserial. |
Responses
| Status | Description | Schema |
|---|---|---|
| 202 | Recorded on every target. | S3InstanceFanoutResponse |
| 207 | Mixed success; see per-node results. | - |
| 401 | Unauthorized. Authentication required. | - |
| 403 | Forbidden. Insufficient permissions. | - |
| 404 | No matching instance. | - |
| 409 | Every target is being deleted (desired=deleting) and cannot be stopped. | - |
| 422 | Validation Error | HTTPValidationError |
| 500 | Internal server error. | - |
Response Body (202, S3InstanceFanoutResponse)
| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
aggregate_status |
integer | yes | n/a | Aggregate HTTP status: the rows’ shared success code (202 for a recorded state change, 200 for a read) if every node succeeded, the shared error code if every node failed with the same code, otherwise 207. delete-all answers 200 once every instance is gone, and start-all/stop-all answer 200 with no results for a tenant with no instances. | |
confirmed |
boolean | no | true |
For delete fan-out, true once the caller has confirmed the destructive operation. When false, the request body enumerates matches without deleting anything. | |
results |
array of S3InstancePerNodeResult object |
yes | n/a |