shipped · unexercised on the hosted instance
Limits: rate, quota, parts, timeouts
Four things bound a request here. Three of them have been driven on this instance; the quota has not, which is why this page carries an amber badge rather than a green one.
Rate: one ceiling per tenant
Section titled “Rate: one ceiling per tenant”Every tenant carries its own rate_limit_rps — a requests-per-second ceiling — and its own token
bucket, keyed by the tenant address. The bucket refills at that rate and its burst allowance is sized
from the same number, with a small floor so that even a modest ceiling still admits the several
requests an S3 client opens for one logical upload.
Where the number comes from:
| How the tenant arrived | Where its ceiling is set |
|---|---|
| Redeeming an invite | On the invite, when the operator minted it. |
| First use of a signed token | The default for the free tier — the same default a redemption gets. |
Read your own with GET /me, or on the portal. It is the field named rate_limit_rps, and it is the
one number the bucket is sized by. Ask for a different one when you ask for an evaluation window.
Over the ceiling, the answer is a 429 carrying retry_after_ms and a Retry-After header —
Headers, status codes, 401 and 429.
Rate isolation holds within one instance. The bucket lives in the process that resolves your credential, so a deployment running several of them would admit a tenant’s full rate independently in each. This is one instance, so today the property holds; it is stated here because it is a property of the deployment rather than of the design.
An overall limit also exists at the edge, across every caller at once. It is a floor under the
whole listener rather than a per-tenant allowance, and it can answer 429 on any route — including
the keyless verify route, which has no tenant to key a bucket by.
Quota: a size check, not a running total
Section titled “Quota: a size check, not a running total”POST /v1/api/custody/store is checked against the tenant’s storage ceiling before the request body
is read, on the size the request declares. Over it:
413 {"error":"quota_exceeded","limit_mib":…,"used_mib":…}Read that check precisely, because it is narrower than the word “quota” suggests:
It is a per-request size check against the ceiling. It is not a running total of what the tenant has stored on this surface. Custody objects are not accumulated into the figure the check compares against, so storing many documents does not walk
used_mibupward.
limit_mib is the tenant’s ceiling and used_mib is what the tenant is already recorded as holding
— both are the caller’s own numbers, which is why they are returned rather than hidden.
This is the unexercised part. Quota is recorded and it is shown, and the check is in the request
path; driving a tenant past its ceiling has not been done on this instance. Treat the 413 as
specified behaviour rather than as witnessed behaviour, and tell us if you drive it.
Custody has a second limiter of its own
Section titled “Custody has a second limiter of its own”Beyond the per-tenant bucket at the edge, the custody surface keeps a local one:
| Bucket | Rate |
|---|---|
Reads and store |
the tenant’s own ceiling |
incinerate |
a fraction of it |
Incineration draws on the tighter budget because it is irreversible. A burst is allowed — a team forgetting several documents back to back is not throttled on the second call — while the sustained rate stays well below the read rate, so a runaway client exhausts its budget long before it gets through a meaningful number of objects.
This limiter is local to the process: it resets when that process restarts, and it is a backstop
beneath the edge’s own limit rather than a replacement for it. Its refusal is the same 429 code
without retry_after_ms.
Multipart: S3’s own part rule applies
Section titled “Multipart: S3’s own part rule applies”Every part except the last must meet S3’s minimum part size. That is S3’s rule, not an extra one
imposed here, and any S3 SDK already enforces it on the client side — but a hand-rolled uploader that
splits a file into equal small pieces will fail at CompleteMultipartUpload rather than at
UploadPart, which is a confusing place to discover it.
The last part may be any size. There is no additional part-count ceiling beyond S3’s own.
Part bytes are not encrypted by this instance; see Encryption at rest and Upload media in parts.
Timeouts: the S3 route is patient
Section titled “Timeouts: the S3 route is patient”CompleteMultipartUpload on a large upload does real work before it answers, and the edge’s timeout
on the S3 route is set to outlast it deliberately — it matches the storage layer’s own. A completion
that takes a while is not going to be cut off at the edge and returned to you as a gateway timeout
after it has already succeeded, which is the failure this setting exists to prevent.
No number is published for it, for the reason the whole site gives no timing figures: the instance is stopped between evaluation windows and runs on testnet, so a figure measured in one window is not a figure to build a retry policy on. Set your client’s own timeout generously on the completion call and normally everywhere else.
- Headers, status codes, 401 and 429 — what each refusal looks like on the wire.
- Tenancy — what one tenant’s ceiling protects it from.
- The testnet instance — what is proven here, and what is not.
Permafrost runs on Sui testnet and Walrus testnet. Everything here describes a shipped testnet instance, not a production service.