Bucket management
https://storage.<region>.basaltic.shSigned with the Basaltic scheme, like every other Basaltic API. Buckets and
their sub-resources live under /v1/buckets.S3 wire protocol
https://objects.<region>.basaltic.cloudSigned as S3 (AWS SigV4). This is what you give to boto3, the AWS CLI, or
anything else that speaks S3.The S3 endpoint is deliberately on a different domain —
basaltic.cloud, not
the product domain. Buckets serve content that gets embedded in other
people’s sites, so that traffic is kept off the domain your console session
and API credentials belong to. Nothing you serve from a bucket can ride a
product-domain cookie.GET /v1/buckets/{bucket}/objects/{key}.
Creating a bucket
3–63 characters
required
Lowercase letters, digits and hyphens. Must start and end with a letter or
digit, must not contain a double hyphen (
--), and must not be shaped
like an IP address.buckets quota; exhausting it is
also a 409 on create.
Object Lock has to be decided here
PUT /v1/buckets/{bucket}/object-lock updates the default retention rule on a
bucket that already has Object Lock enabled. Against a bucket that does not, it
is a 409.
Pointing an S3 client at it
Set a custom endpoint and sign with your Basaltic access key. Nothing else about the client changes.Credentials
Credentials
The same access keys you use everywhere else. A service account’s long-lived
key needs nothing extra; temporary credentials from STS — a role session or
a user session — must also carry the session token, and are rejected
without it.See authentication for how to obtain each.
Addressing style
Addressing style
Both styles work. Virtual-hosted
(
https://my-app-assets.objects.sa-saopaulo-1.basaltic.cloud/key) is what
most SDKs default to; path-style
(https://objects.sa-saopaulo-1.basaltic.cloud/my-app-assets/key) is
available through your client’s addressing-style option.The region string
The region string
Set
region_name to the Basaltic region code. The value is not checked
against the region serving the request — it only has to match what your
client signed with — so a tool hard-wired to us-east-1 still works.
GetBucketLocation reports the real region.Clock skew and presigned URLs
Clock skew and presigned URLs
A signed request must be within 15 minutes of the server’s clock, or it
is rejected as too skewed. Presigned URLs are supported with an expiry
between 1 second and 7 days, and a URL dated in the future beyond the
skew tolerance is refused rather than becoming valid later.
What the S3 endpoint serves
The endpoint is verified against a real AWS SDK rather than a specification of our own — if boto3 can do it and gets S3’s error codes back, it works. What is routed today:Buckets
ListBuckets, CreateBucket, HeadBucket, DeleteBucket, GetBucketLocation, and
the
?policy, ?cors, ?lifecycle, ?versioning, ?encryption,
?tagging, ?object-lock and ?acl sub-resources.Objects
PutObject, GetObject (including range requests), HeadObject, DeleteObject,
DeleteObjects, CopyObject, ListObjects, ListObjectsV2, ListObjectVersions,
and the
?tagging, ?retention, ?legal-hold and ?acl sub-resources.Multipart
CreateMultipartUpload, UploadPart, UploadPartCopy, ListParts,
ListMultipartUploads, CompleteMultipartUpload, AbortMultipartUpload.
Payload signing
Signed payloads,
UNSIGNED-PAYLOAD, and signed aws-chunked streaming
uploads. The body is re-hashed as it streams, so a body that does not match
what was signed is rejected mid-flight.NotImplemented. Preflight OPTIONS
requests are answered without a signature, because browsers never sign them.
Objects and their limits
These are S3’s own numbers, and they are enforced at S3’s values:
The 5 MiB floor applies to every part except the last one named at completion,
and it is checked at completion, not when the part is uploaded. A part list
whose non-final part is short fails with
EntityTooSmall after the bytes are
already staged.
The assembled object is written out in full inside the request that completes
the upload. Budget roughly 85 MiB/s for that write, and note that the
public edge ends any request after one hour. Objects beyond a few hundred
GiB need a completion path this API does not offer yet.
abort_incomplete_multipart_upload lifecycle
rule rather than relying on remembering.
Storage classes
Two classes, and they name device tiers rather than access patterns:
They keep S3’s uppercase spelling because the vocabulary is S3’s. Set a class
per object with
x-amz-storage-class on the upload, or move objects between
classes with a lifecycle transition. The two classes bill separately, so the
label an object carries decides which counter and which price its bytes land on.
An unsupported class — STANDARD_IA, GLACIER, anything else S3 defines — is
rejected rather than quietly stored as STANDARD.
Versioning
PUT /v1/buckets/{bucket}/versioning moves a bucket between three states:
never configured
The bucket has no versioning history. On the S3 wire this is reported by
omitting the status element entirely, which is how S3 itself reports it.
every write creates a version
Deletes write a delete marker instead of removing bytes.
GET /v1/buckets/{bucket}/object-versions lists versions and delete markers
together.was on, now off
Existing versions are kept; new writes stop creating them. This is
distinct from
disabled, and the distinction matters: suspending does not
delete history.Enabled and Suspended in its XML. Either spelling is
accepted on input.
Object Lock and retention
On a bucket created withobject_lock_enabled, individual objects can carry a
retention mode and a retain-until date, set at upload with
X-Amz-Object-Lock-Mode and X-Amz-Object-Lock-Retain-Until-Date, or afterwards
through the ?retention sub-resource.
GOVERNANCE
Retention can be shortened or a locked object deleted, but only by a caller
who sends
X-Amz-Bypass-Governance-Retention: true and holds
storage:BypassGovernanceRetention on the object.COMPLIANCE
Nothing bypasses it. A
?retention write that would move the date earlier
is refused, so retention can only ever be extended.?legal-hold) is independent of the retention date: while it is
on, the object cannot be deleted regardless of when retention expires. A delete
blocked by either shows up as 403, not a silent no-op.
Encryption
Server-side encryption is opt-in, and it is off until you turn it on. Two ways:AES256 is supported; aws:kms and
customer-provided keys are rejected with a clear error rather than being
silently downgraded.
A multipart upload carries no encryption header of its own on each part: the
decision is made when the upload is created and survives to the assembled
object. Check
ServerSideEncryption on a HEAD of the finished object, not
on the create response.Lifecycle rules
PUT /v1/buckets/{bucket}/lifecycle replaces the whole configuration. Each rule
needs a status and at least one action; a rule with status: disabled stays in
the configuration but is skipped during evaluation.
Rules are evaluated independently, so an object matching several is subject to
all their actions.
filter.prefix narrows a rule; an absent or empty filter
applies it to the whole bucket.
A transition keeps the object’s identity — same key, same version id, same
last-modified — and moves only its bytes. That is what makes pairing a
transition with an expiration work: the move does not restart the expiry
clock.
- A transition must come strictly before the rule’s expiration. Otherwise the object would be deleted before it ever moved.
- One transition per rule. With two storage classes a second has nowhere to
go. The S3 endpoint accepts a single-element
<Transition>list and rejects longer ones rather than applying the first and ignoring the rest.
Quotas
Two counters, both scoped to your organization within a region:object_storage_gb is enforced by comparing what you actually store against the
limit, not by a running counter. That means space freed by a lifecycle
expiration, a version delete or a bucket purge comes back on its own — there is
no counter left holding a phantom charge against your cap.
Access control
Two independent layers decide every object request:IAM policies
What your own principals may do, written against
crn:storage:<region>:<account>:bucket/<name> for the bucket and
crn:storage:<region>:<account>:bucket/<name>/<key> for an object. See
writing policies.The bucket policy
A document attached to the bucket itself, at
PUT /v1/buckets/{bucket}/policy. This is what grants access to principals
outside your account — including anonymous readers.deny in either layer
wins outright; otherwise an allow from either layer is enough to proceed.
Object tags are policy context, so access can be fenced on the object itself:
s3:ExistingObjectTag/<key> matches what is already on the object,
s3:RequestObjectTag/<key> what a write is trying to set, and
basalt:TagKeys is the set of keys a request carries.
Both layers stop applying the moment the bucket owner’s organization is
suspended — cross-account grantees and anonymous readers of a public bucket
included.
Deleting a bucket
DELETE /v1/buckets/{bucket} does two quite different things depending on
whether deletion protection is on:
- Protection off (default)
- Protection on
The bucket must be empty. Any remaining objects, versions or in-flight
multipart uploads make it a
409 BucketNotEmpty. An empty bucket is
deleted immediately and the quota slot is released.recovery_days is clamped to 1–30; 0 uses the default of 7 days.
Working with objects through the storage API
You do not need an S3 client. The storage API exposes the same object plane under/v1/buckets/{bucket}/objects, signed the Basaltic way like every other
API call — useful for a service that already holds Basaltic credentials and
should not carry an S3 SDK:
?tagging,
?retention, ?legal-hold — and the same X-Amz-* headers apply on upload for
storage class, encryption and object-lock settings. delimiter gives you the
usual folder semantics through common_prefixes, and max_keys is capped at
1000 per page.
Bulk delete (
POST ?delete) and server-side copy (x-amz-copy-source) are S3
endpoint features. Through the storage API, delete objects one at a time.Troubleshooting
SignatureDoesNotMatch
SignatureDoesNotMatch
The secret does not match the access key, or something the client signed
was rewritten in transit. Check first that the credential is right, then
that your client is signing with SigV4 (
signature_version="s3v4") — the
endpoint accepts nothing older.A key or query value containing a space or a + is a classic case: those
have to be percent-encoded in the canonical request, and an SDK doing it
correctly will interoperate.RequestTimeTooSkewed
RequestTimeTooSkewed
Your clock is more than 15 minutes from the server’s. Fix time sync on the
machine making the request; there is no way to widen the window.
AccessDenied on a bucket you own
AccessDenied on a bucket you own
An explicit
deny in either layer wins, so start there: a bucket
policy denying something is enough to block a caller their IAM policies
allow, and the reverse holds too. With no explicit deny anywhere, you need
an allow from one of the two — a bucket policy that names other principals
is not itself a denial, but it will not stand in for the IAM grant you are
missing.Deleting an object also fails with 403 when it is protected by an active
retention period or a legal hold, which reads like a permission problem but
is not one.BucketNotEmpty on delete
BucketNotEmpty on delete
Objects, versions, or in-flight multipart uploads remain. List uploads with
GET /v1/buckets/{bucket}/multipart-uploads — an abandoned upload holds
the bucket open just as an object does, and does not show in an object
listing.EntityTooSmall at completion
EntityTooSmall at completion
A part other than the last one is under 5 MiB. The floor is checked when
the upload completes, so this surfaces after every part has been staged.
Re-upload with larger parts.
The object is not encrypted despite the bucket default
The object is not encrypted despite the bucket default
A bucket default applies to writes made after it was set; existing
objects are untouched. Re-upload anything already stored if you need it
covered. Check
ServerSideEncryption on a HEAD of the stored object,
which is the only answer that reflects what was written.S3 error codes
The S3 endpoint answers in S3’s own XML vocabulary, so an SDK’s error handling works unchanged:Next
Block storage
Volumes, snapshots and snapshot policies.
Authentication
Access keys, temporary credentials and session tokens.
Writing policies
IAM statements, bucket policies and tag conditions.
API reference
Every bucket operation, with request and response schemas.