Sapherbox logoSapherboxBlog
All articles

Your tenant, your bucket

Every customer gets a deployment of their own, and the encrypted files can live in a bucket you own. What that isolates, what a bucket operator sees, and how moving between buckets works.

Also available in Deutsch

Most cloud document platforms are one large system with a column that says which customer a row belongs to. That design is efficient, and it means every customer’s security rests on the same access-control code never making a mistake. Sapherbox is built the other way: each customer gets a deployment of their own, and the object storage that holds the encrypted files can be a bucket the customer owns. This article explains what that buys you, what it does not, and the mechanics of bringing and changing a bucket.

One tenant, one deployment

A Sapherbox tenant is a separate deployment of the whole application: its own API, its own web client, and a database of its own with audit logging enabled at the database level. It has its own hostname and TLS certificate, its own secrets, its own e-mail sending identity, and its own object-storage bucket. There is no database that holds more than one customer, so there is no query in which a missing filter could show one customer another’s rows.

The isolation is also operational. A tenant is upgraded, backed up and, if it comes to that, deleted as a unit. A problem in one tenant’s deployment is one tenant’s problem. The price is that we run more deployments than a multi-tenant design would need, and we consider that the right price for a product whose point is that the operator cannot see inside.

What is in the bucket

The bucket holds one kind of object, and it is worth saying what it does not hold.

File content, as ciphertext. Every file is encrypted on the device that uploaded it with a key the server never has, and the bucket receives the result under a random object key. Large files are stored in encrypted chunks of a fixed size. An operator of the bucket, whether that is us, Amazon, or your own storage team, sees object names that are identifiers, object sizes, timestamps and nothing else. Not the file name, which is encrypted too, and not a byte of content.

Nothing else. Backups do not go here. The tenant’s database is dumped once a day, compressed and encrypted with age to a public key whose private half is kept offline and exists on no server, and that archive is written to a separate backup bucket in our account. The tenant’s credentials for that bucket are write-only: they can add an object, not read, overwrite or delete one, so a compromised tenant cannot reach back into its own history. The backup bucket itself is object-locked, so a dump cannot be deleted by anyone, us included, before its retention has run out. File objects are not copied anywhere. Their durability is the storage bucket’s job, which is the next section. Someone with the backup bucket’s credentials could download a database backup; they could not open it. The database itself contains sealed keys, public keys, encrypted names and the metadata we publish as visible to the server, so even a decrypted backup would not yield documents, but the age layer means the bucket is not where that question gets decided.

A new tenant starts with a bucket we provision in AWS Frankfurt, with server-side encryption and public access blocked, as a second layer under the client-side encryption. That is the starting point, not a commitment: every customer can configure buckets of their own, as the next section describes. That layer is worth having and worth not relying on: it protects against a disk leaving a data centre, not against anyone holding the bucket credentials.

Bringing your own

A tenant administrator can add storage locations under Enterprise settings. Three kinds are supported: Amazon S3, Google Cloud Storage with a service-account key, and any S3-compatible endpoint, which covers Hetzner Object Storage, a MinIO cluster in your own data centre, Wasabi, and the storage tier of most European hosters. For Amazon S3 the region has to be one of the EU regions; the tenant refuses anything else. For the other kinds, the location is whatever you created the bucket in, which is your decision to document.

The credentials you enter are stored in the tenant database field-encrypted under a key that lives in the tenant’s secret store, not in the application configuration. A Test button makes the tenant open a connection to the bucket and report back, so a typo is found before any upload depends on it.

Several locations can exist at once. One is the default and receives every new upload. Files already stored elsewhere stay where they are and keep working, because each file records which location holds it. That is what makes a gradual change possible: add your bucket, test it, make it the default, and from that moment new documents land in your bucket while the old ones are still served from ours.

Moving what is already there

For the files that were uploaded before the switch there is a copy-or-move operation between any two locations. It runs in the background on the tenant, file by file: read the encrypted object from the source, write it to the destination, update the file’s record to point at the new location, and with a move, delete the source object afterwards. The tenant never decrypts anything during this, because it cannot. Progress and any per-file errors are visible to the administrator while it runs, and a failed file is left where it was rather than half-moved.

The same operation runs in the other direction, which matters for exit. If you leave Sapherbox, or merely leave our bucket, the ciphertext is already in storage you control, and the keys to it were always on your users’ devices. Nothing has to be requested from us to get your data out; what you cannot do without the users is turn it back into documents, which is the property you were paying for.

What this changes for compliance

Three things, in practice.

The sub-processor list. When files live in your bucket, the object-storage provider is your contract, not ours, and drops off our list for your tenant. Our data processing agreement still covers the tenant itself, where the ciphertext passes through on its way to and from the bucket.

Residency, with the right footnote. Your bucket is where you put it, under terms you negotiated. The honest footnote is that residency of ciphertext is a weaker claim than it sounds and a stronger one than usual: weak because the location of encrypted bytes matters little to anyone who cannot decrypt them, strong because the keys were never anywhere but your users’ devices in the first place. Hosting in the EU is a topic of its own and we will write it up separately.

Your retention, your durability. The bucket we provision has versioning and a 30-day object lock on every version, in what AWS calls governance mode. When Sapherbox deletes an object, the bucket keeps the previous version for 30 days, and during that time no credential can remove it, not the tenant’s, not ours in day-to-day operation. After 30 days a lifecycle rule removes it, and the deletion is final, as described in the deletion article. The tenant’s own credentials are explicitly denied the actions that would shorten this: deleting versions, bypassing the lock, changing the bucket’s configuration. Only we can change the window, and we do so on request, in either direction, for example to honour an erasure request earlier or to lengthen the history. That is the point of governance mode rather than compliance mode: the backup bucket uses compliance mode because a backup must never be deletable early, but your storage bucket must still be able to honour the right to erasure. Replication to a second region is set up the same way. In your own bucket you set all of this yourself, according to your own policy, and the tenant does not need to know. That is the difference: not what is possible, but who holds the controls.

Limits, stated

  • One bucket per location, one region per bucket. Sapherbox itself does not replicate between regions; that is a bucket feature. On the bucket we provision, which lives in our AWS account, we configure replication for you on request. In your own bucket you configure it yourself.
  • The tenant still handles the bytes. Uploads and downloads go through the tenant’s API rather than straight to the bucket, so that every access is authenticated, authorised and audited in one place. The tenant sees ciphertext in transit, nothing more, but it is in the path, and its bandwidth is the ceiling.
  • Moving is per file and takes time. A migration of a large tenant runs for hours. Nothing is unavailable meanwhile, because every file is served from wherever its record says it is.
  • Backups stay with us, ciphertext stays where it is. The daily database dump is written by the tenant to the separate, write-only backup bucket, not to your storage location, and it is encrypted to our offline key. File content is never copied out of the storage bucket; versioning and object lock there are what protects it. Restoring a tenant therefore needs both sides: our backup and key, and the bucket with the ciphertext, ours or yours. That is the correct dependency and worth knowing before the day you need it.

Questions?

If you want to run a tenant against your own storage, or your security team wants the exact object layout and the IAM policy the tenant needs on your bucket, write to hello@sapherbox.com.