Skip to content

Customers and isolation

A customertenant in the code — is the platform's unit of isolation. Every object belongs to one: its devices, its agents, its credentials, its backups, its logs, its reports.

What a customer owns outright

Creating a customer is not merely adding a row to a table. The customer receives:

Element Consequence
A storage prefix its objects live under tenants/<slug>-<suffix>/, apart from everyone else's
Its own encryption key its backups are readable only with it
Its own deduplicated repository deduplication never operates across two customers
A service account on the store confined to its prefix, with no delete permission

Prefix and repository key

The prefix carries a random suffix: acme becomes tenants/acme-06d37b2a/. Two customers with the same name at two providers, or a customer recreated after deletion, therefore never end up writing to the same place.

The repository key is envelope-encrypted under the platform master key (BKP_MASTER_KEY) and exists in clear only in memory, long enough to hand an agent the elements of a job. It is what makes crypto-shredding possible: destroying that one key makes the customer's entire backup set unreadable on the spot.

The service account

The customer also receives its own identity on the object store — the account with which its agents write. It is not the platform's account: it is confined to that customer's prefix, it cannot read another customer's objects, and it holds no delete permission at all.

Without it, the agents fall back to the platform credentials. They keep working, and that is exactly what makes the gap easy to miss: backups succeed, the screens stay green. But an agent then holds an identity that can reach every customer's prefix. Storage isolation no longer rests on anything but encryption, and immutability on nothing but the object lock — two layers where there should be three, and the one that disappears is the one nobody can see fail. A compromised agent host, or a collector writing to the wrong prefix, stops being contained by the store itself.

A customer with no service account is flagged on its own page, under Storage, and by Settings → Storage compliance.

Creating it. It is created with the customer. When the object store was unreachable at that moment, catch up from Customers → the customer → Storage → Create the service account, or from the command line:

backup tenant provision acme

Renewing it. The same gesture renews. The object store never gives back the secret of an existing account, so renewing always issues a new one and the previous key stops being accepted at once. Agents receive the new key with their next job; a backup already running with the old one fails and is retried. Renew when an agent host has been compromised or decommissioned — the key it held is then no longer worth anything.

When per-customer identities are turned off. BKP_S3_PER_TENANT_IDENTITIES exists for a development environment with no object store admin API. Turned off, no service account is created and every agent writes with the platform credentials — a posture, not an accident, and the storage compliance check reports it as such.

Why deduplication does not cross customers. A shared deduplicated repository would let someone write a piece of content and observe whether it is stored or merely referenced — confirming that another customer already holds it. The space saved is not worth that side channel.

Creating a customer

The customer list: devices, agents, state and contact — a customer with no service account is flagged there

From the interface: Customers → + Customer. Three fields.

Field Constraint Purpose
Short name (slug) lower case, digits and hyphens, 2 to 63 characters serves as storage prefix and command-line reference
Legal name free what is displayed everywhere
Contact optional recipient of the reports

The short name cannot be changed: it is carved into the storage prefix.

On the command line:

backup tenant create acme "ACME Industries" --email noc@acme.example

Also via the API.

curl -s -X POST "$BASE/api/tenants" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"slug":"acme","name":"ACME Industries","contact_email":"noc@acme.example"}'
Requires the tenant:manage permission, hence the platform_admin role.

If the object store is unreachable

Creation fails explicitly rather than leaving a half-made customer. A customer without its service account would have its agents writing with the platform credentials, and nothing on screen would say so. Catch up from the customer's page, or:

backup tenant provision acme

Also via the API.

curl -s -X POST "$BASE/api/tenants/$CUSTOMER/provision" -H "Authorization: Bearer $TOKEN"

Onboarding a customer: customer and agent in one step

The everyday gesture is not “create a customer” but “take on a customer”. The Agents → + Agent screen therefore lets you create the customer and its first agent in the same panel, then issue the enrolment token straight after. That is the recommended path for a new customer; see Agents.

A customer's page

It gathers what decides its service:

  • that customer's fleet status: devices, compliant, in trouble, coverage;
  • storage: prefix, service account, measured usage, trend and estimated saturation date (see Capacity);
  • the fleet breakdown by platform;
  • its agents and its devices;
  • its trust posture and its retention, showing where each value in the cascade comes from;
  • the withdrawal zone, kept apart at the foot of the page.

Withdrawing a customer

A customer is never deleted. Its page remains as a record: proving that and when you deleted is part of deleting, and it is what an auditor will ask for.

Withdrawal happens in three distinct steps, only one of which is reversible.

Step Effect Reversible
Withdrawal from service no more scheduling, agents revoked, queue cleared yes
Key destruction backups unreadable immediately, including for the provider no
Storage purge bytes erased, space returned no
backup tenant decommission acme   # 1. reversible
backup tenant shred acme          # 2. irreversible
backup tenant purge acme          # 3. once the lock expires

Each step requires typing the customer's short name. This is not decoration: it is the only operation on the platform whose effect becomes irreversible within minutes, and a yes/no confirmation is not enough to commit to it.

Also via the API.

# Withdrawal — reversible. “confirm” must equal the customer slug.
curl -s -X POST "$BASE/api/tenants/$CUSTOMER/decommission?confirm=acme" \
  -H "Authorization: Bearer $TOKEN"

# Put back in service
curl -s -X POST "$BASE/api/tenants/$CUSTOMER/reinstate" -H "Authorization: Bearer $TOKEN"

# Key destruction — irreversible, requires the “purge:force” permission
curl -s -X POST "$BASE/api/tenants/$CUSTOMER/shred?confirm=acme" \
  -H "Authorization: Bearer $TOKEN"

Why destroy the key before erasing the bytes

A customer asking for its data to be deleted cannot be served immediately at the byte level: the object lock forbids any deletion for its floor duration, and that is precisely its job in the face of ransomware. The maintenance policy forbids itself from bypassing it.

Destroying the key resolves the contradiction. The repository becomes unusable on the spot; space is released when the technical deadline passes. That stands up contractually as it does in front of an auditor: the data is unrecoverable at the moment you announce it.

The purge delay (tenant_purge_after_days, 45 days by default) must stay greater than the lock floor (object_lock_days, 30 days by default), or deletions would fail every time. The platform says so at withdrawal if that is not the case.

What a customer account can see

Customers have no identity federation: they get local accounts confined to their own scope. Such an account cannot discover that another customer exists — not through a partial name search, nor through an identifier guessed in a URL. See Security model and Accounts.