The S3 endpoint
Updated October 26, 2026
Use Terebyte from any program or library that speaks Amazon S3: the address, keys, what's supported, two short examples, and what isn't supported.
The address
https://s3.terebyte.com/<bucket>/<key>- Path style only. Set
forcePathStyle: true(JavaScript) oraddressing_style: "path"(Python). Bucket names as subdomains (<bucket>.s3.terebyte.com) aren't supported. - Region: any region name works; it's read from the signature. Use
us-east-1if your library needs one. - Requests are answered by Terebyte's download servers, dl1 (Virginia) and dl2 (Oregon). Bytes go between them and storage directly; they never pass through the website.
Keys
Make a key on Rooms in the sidebar, under S3 keys: give it a name (up to 60 characters, such as the program that uses it) and choose a mode.
- Access key ID:
TBKfollowed by 17 characters, such asTBKXXXXXXXXXXXXXXXXX. - Secret: shown once, when the key is made. We don't store it. Copy it into your program's settings or a secrets manager. If you lose it, revoke the key and make a new one.
- Revoke a key on the same page; it's refused from then on.
- A key reaches its own account's buckets only: never a room, never a vault.
Modes
| Mode | What it stores | Who could read it |
|---|---|---|
| Sealed | Only Terebyte's sealed-object format: your program encrypts before it uploads | Only you |
| Plain | Objects exactly as your program sends them | We could read them. Unlike everything else in Terebyte, plain objects are not end-to-end encrypted. Use plain only for what you'd put in any cloud. |
Plain keys and their buckets are labeled "Plain" wherever they appear. A sealed key refuses any body that isn't in the sealed-object format, with AccessDenied.
In both modes, bucket names, object names, sizes, ETags and content types are stored as plain details in our database (an object's bytes are stored under a random name, never its own).
Signatures
- AWS Signature Version 4, in the
Authorizationheader or presigned in the URL. - Presigned URLs last from 1 second to 7 days.
- A request's time must be within 15 minutes of ours.
- Bodies: a signed SHA-256 payload hash (checked as the bytes arrive; an object whose hash doesn't match is deleted at once),
UNSIGNED-PAYLOAD, and theaws-chunkedbodies newer SDKs send (STREAMING-UNSIGNED-PAYLOAD-TRAILER,STREAMING-AWS4-HMAC-SHA256-PAYLOAD). - Every upload needs its length (
Content-Length, orx-amz-decoded-content-lengthforaws-chunked).
Supported operations
| Operation | Notes |
|---|---|
| ListBuckets | |
| CreateBucket | Up to 100 buckets an account |
| HeadBucket, GetBucketLocation | The location is empty (the default region) |
| DeleteBucket | Only an empty bucket |
| PutObject | Replaces an object with the same key |
| GetObject | With byte ranges (bytes=a-b, bytes=a-, bytes=-n) |
| HeadObject | |
| CopyObject | Within the same account |
| DeleteObject | |
| DeleteObjects | Up to 1,000 keys a request; Quiet supported |
| ListObjectsV2 | prefix, delimiter, continuation-token, start-after, max-keys (up to 1,000) |
| ListObjects (version 1) | prefix, delimiter, marker, max-keys |
| CreateMultipartUpload, UploadPart, CompleteMultipartUpload, AbortMultipartUpload | Parts numbered 1 to 10,000, each up to 5 GiB |
| ListMultipartUploads | Always answers with an empty list |
Example: AWS SDK for JavaScript (v3)
import { S3Client, CreateBucketCommand, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: "https://s3.terebyte.com",
region: "us-east-1",
forcePathStyle: true,
credentials: {
accessKeyId: "TBKXXXXXXXXXXXXXXXXX",
secretAccessKey: process.env.TEREBYTE_S3_SECRET, // never in your code
},
});
await s3.send(new CreateBucketCommand({ Bucket: "reports" }));
await s3.send(new PutObjectCommand({ Bucket: "reports", Key: "2026/october.txt", Body: "Hello", ContentType: "text/plain" }));
const got = await s3.send(new GetObjectCommand({ Bucket: "reports", Key: "2026/october.txt" }));
console.log(await got.Body.transformToString());Example: boto3 (Python)
import os
import boto3
from botocore.config import Config
s3 = boto3.client(
"s3",
endpoint_url="https://s3.terebyte.com",
region_name="us-east-1",
aws_access_key_id="TBKXXXXXXXXXXXXXXXXX",
aws_secret_access_key=os.environ["TEREBYTE_S3_SECRET"], # never in your code
config=Config(signature_version="s3v4", s3={"addressing_style": "path"}),
)
s3.create_bucket(Bucket="reports")
s3.put_object(Bucket="reports", Key="2026/october.txt", Body=b"Hello", ContentType="text/plain")
print(s3.get_object(Bucket="reports", Key="2026/october.txt")["Body"].read())
for page in s3.get_paginator("list_objects_v2").paginate(Bucket="reports", Prefix="2026/"):
for item in page.get("Contents", []):
print(item["Key"], item["Size"])Keep signature_version="s3v4" in boto3's config: without it, boto3's presigned links use the old signature (version 2), which this endpoint refuses (403). The AWS SDK for JavaScript signs with version 4 already.
Both examples store Hello as written, so they need a plain key. With a sealed key, the body must already be in the sealed-object format.
Errors you may see
| Code | HTTP | Meaning |
|---|---|---|
InvalidAccessKeyId | 403 | No such key, or it was revoked |
SignatureDoesNotMatch | 403 | Wrong secret, or the request changed after signing |
RequestTimeTooSkewed | 403 | Your clock is more than 15 minutes off |
AccessDenied | 403 | A presigned URL expired, or a sealed key was sent a body that isn't sealed |
NoSuchBucket, NoSuchKey, NoSuchUpload | 404 | |
BucketNotEmpty, BucketAlreadyOwnedByYou | 409 | |
InvalidBucketName, KeyTooLongError, TooManyBuckets, EntityTooLarge | 400 | See limits |
XAmzContentSHA256Mismatch | 400 | The body isn't the one signed; nothing is kept |
MissingContentLength | 411 | The upload didn't say its length |
QuotaExceeded | 507 | Not enough space left on the plan, or a developer account's credits ran out |
SlowDown | 503 | Too many requests from your network, or storage is paused: try again later |
NotImplemented | 501 | An operation that isn't supported |
Who pays
- On a member's account, objects count toward the member's space, and downloads toward the member's downloads, like any file.
- On a developer account with credits, storage and downloads are paid from credits. See credits and plans.
- Requests are free. Usage per key and per month is on the usage page (
/agents/usage), with a CSV export: totals only, never object names.
Not supported
- Virtual-host addresses (
<bucket>.s3.terebyte.com). - Versioning, ACLs, bucket policies, CORS, lifecycle rules, tagging and encryption settings (
NotImplemented). - ListParts (
GETwithuploadId) answersNotImplemented. - Copying from another account's bucket.
- Object Lock, storage classes other than STANDARD, and POST uploads from browser forms.
What the endpoint doesn't do
- Sealed keys from your own program. A sealed key accepts only Terebyte's sealed-object format, whose description isn't published, so programs outside Terebyte should use a plain key (and know we could read what it stores).
- S3 through a door. An S3 key reaches its account's own buckets; it never reaches a room.