Purpose
Explains how your data is encrypted on this gateway. There are two separate things
people mean by “encryption,” and the gateway answers both: how your bytes are
protected on disk (always on, nothing to configure) and whether your S3
client can see the bucket is set for server-side encryption (the standard S3
contract, opt-in). Everything below is on by default unless it says otherwise; you do not
supply keys or set headers to be protected.
Content summary
Always-on storage encryption — every file you upload is encrypted with
XChaCha20 (256-bit key, 192-bit nonce) before any byte is written to storage.
The ciphertext is sharded into 480 MB rows; each row becomes 80 data shards + 40
parity shards (120 shards) placed on 120 different Storage Provider nodes. A file larger
than 480 MB spans multiple rows, and each row is placed independently, so one upload
can reach far more than 120 providers. HostIO continuously goal-seeks the best-performing
providers within your Virtual Private Datacenter selection and rotates shards as
conditions change, so over time a dataset ends up spread across hundreds of providers. It
cannot be turned off — there is no unencrypted storage path, and there hasn’t been since
the product launched in 2019.
The S3 encryption contract — many S3 tools and policies expect to read an
explicit server-side-encryption setting on a bucket. The gateway implements the standard
S3 Default Encryption contract with algorithm AES256 (SSE-S3) so those tools
are satisfied. Setting it is a declaration your client can read back; it does
not add a second encryption layer and does not change how your data is stored.
Modes we don’t offer are rejected, not silently ignored. aws:kms
and SSE-C (customer-supplied per-object keys) return 501 NotImplemented, so
your tool gets a clear error instead of assuming a mode that was never applied.
Always-on storage encryption
When you upload a file, the gateway encrypts its bytes with XChaCha20
(256-bit key, 192-bit nonce) before anything is written to storage. The ciphertext is
sharded into 480 MB rows; each row is split into 80 data shards and 40 parity shards
(120 shards per row), and each shard lands on a different Storage Provider node. Three
consequences follow:
- No single provider ever holds a readable copy. Compromising one
provider — or a handful — yields only ciphertext fragments that cannot be reassembled
without the remaining shards and the key. - The provider set is dynamic. The nodes holding any given row are
the ones HostIO currently rates best for it, so there is no static topology to plan
around. - You cannot turn it off and never had to turn it on. Pre-Shard
Encryption is a property of the write path, not a bucket or object setting. There is no
“plaintext mode.”
You do not supply keys, configure a policy, or set headers. Upload the file; it is
encrypted.
The S3 encryption contract
To satisfy S3 clients and organizational policies that expect an explicit
server-side-encryption declaration, the gateway implements the standard S3 Default
Encryption contract with algorithm AES256 (also called SSE-S3):
PUT /{bucket}?encryption— sets the bucket default to
AES256.GET /{bucket}?encryption— returns the bucket encryption configuration,
orServerSideEncryptionConfigurationNotFoundErrorif none is set.DELETE /{bucket}?encryption— clears the bucket default.
Setting the default to AES256 records the bucket as configured for SSE-S3 — a
contract your client can read back. It does not stack a second cipher on top of the
always-on storage encryption; your data is protected the same way either way. Use the
gateway without ever calling PutBucketEncryption and your data is still
protected; set AES256 if your workflow needs the S3 declaration. Both are true
at once.
If your S3 client sends an SSE header
Some clients send an x-amz-server-side-encryption header on
PutObject or set a --sse flag. The gateway handles each value
like this:
AES256— Accepted. The object is stored exactly as it
would be with no header (already protected), and the S3 contract is honored.aws:kms— Rejected with501 NotImplemented.
The gateway does not operate a KMS backend.- SSE-C headers (
x-amz-server-side-encryption-customer-algorithm) —
Rejected with501 NotImplemented, by design. SSE-C asks you to
send your key in a header on every request, so it crosses the wire each time and sits in
server memory while the work happens. Your bytes are already XChaCha20-encrypted on every
upload with no header required, so that key would buy you nothing and cost you custody.
If you want sole custody of a key, encrypt before you upload — we store the ciphertext
and never inspect it, so your key never reaches us at all.
You never need --sse to protect your data — it is already protected. Pass
--sse AES256 only if your tool or policy language requires the declaration.
Tool compatibility
| Tool | --sse required? | Behavior with unsupported modes |
|---|---|---|
| aws-cli | No | --sse aws:kms and --sse-c return 501 NotImplemented. |
| rclone | No | server_side_encryption = aws:kms returns 501 NotImplemented; AES256 is accepted. |
| restic | No | Sends no SSE headers by default; no action required. |
| kopia | No | Sends no SSE headers by default; no action required. |
| mc | No | mc encrypt set sse-kms returns 501 NotImplemented; sse-s3 is accepted. |
| veeam | No | SSE-S3 (AES256) accepted; KMS and SSE-C return 501 NotImplemented. |
| boto3 | No | ServerSideEncryption='AES256' accepted; 'aws:kms' and SSE-C parameters return 501 NotImplemented. |
The short version: --sse is never required to protect your data. If you set it
anyway, use AES256.
For security reviewers
Facts below are stated conceptually; do not rely on any specific internal column or schema
name, which may change between releases.
- Cipher at rest. XChaCha20, 256-bit key, 192-bit nonce. No
alternative storage cipher, no plaintext mode, no opt-out. It is a property of the write
path, not a bucket or object setting. - Key material. Managed by the gateway; never transmitted to the
customer and never stored alongside ciphertext shards on the same Storage Provider
node. - Shard topology. 480 MB rows, 80 data + 40 parity (120) per row,
across 120 distinct nodes. Files over 480 MB span multiple independently-placed
rows, so one object can reach many more than 120 providers. HostIO goal-seeks the best
providers within the customer’s Virtual Private Datacenter selection and rotates shards
as conditions change; over a dataset’s life the provider set grows into the hundreds. - SSE-S3 contract scope.
PutBucketEncryptionwith
AES256records the bucket as configured for SSE-S3. Per-object SSE headers
are tracked with the object soGetObjectreports the requested and effective
mode back. - Rejections.
aws:kmsand SSE-C return
501 NotImplemented. The gateway never silently accepts a KMS request and
falls back — client expectation and gateway behavior cannot diverge without the client
being told. - Audit trail. Every SSE-affected request is logged with bucket,
object, requested mode, effective mode, and outcome, so operators can reconstruct the
decision after the fact.
To read a bucket’s current SSE configuration from a tool, use GetBucketEncryption.
To verify always-on storage encryption is in effect: it is — for every bucket, every
object, every upload.
History
- 2019 — Product launch. Pre-Shard Encryption has protected every
object since; there has never been an unencrypted storage path. - 2026 — S3 Default Encryption contract added.
PutBucketEncryption,GetBucketEncryption, and
DeleteBucketEncryptionimplement theAES256path; SSE-C and
aws:kmsare explicitly rejected with501 NotImplemented.
