genv.

PlatformMulti-tenant permissions for hosting teams

10 min readPublished Reviewed By the GENV team

In short

Separate commercial authority from operational authority. The people who take money are not automatically the people who run servers. Then make every workspace boundary absolute: a record from another workspace must be indistinguishable from one that does not exist.

Permissions in a hosting platform have two independent axes, and it is easy to model only one. The first is which business are you acting for — the tenancy boundary. The second is what may you do within it — the role. Getting the first wrong is a data breach; getting the second wrong is an operational accident.

The tenancy boundary is absolute, and it has a specific shape

The rule that matters: a record belonging to another workspace must answer exactly as a record that does not exist. Not "forbidden", not "you do not have access to this invoice" — the same not-found a nonexistent identifier gets.

The reason is that a distinguishable refusal is an oracle. If "forbidden" means it exists and "not found" means it does not, anybody with a login can enumerate your other customers' record identifiers and learn how many invoices a competitor has. Two different answers leak, and the leak is cheap to avoid.

This has to hold on every route, including the ones added last. The usual failure is a new endpoint that checks the record exists before it checks who is asking.

Roles: commercial and operational are different

A role set that survives contact with a real team
RoleTypically mayTypically may not
OwnerEverything, including who else has access—
AdministratorOperational and commercial workChange ownership of the business
BillingIssue invoices, record payments, place a non-payment holdSuspend hosting, read provider credentials, open a customer's panel
SupportAnswer and assign conversations, read servicesIssue refunds, change prices, connect providers
Read-onlySee the registersChange anything

The billing row is the interesting one. A bookkeeper needs to place a non-payment hold — that is a commercial act — and does not need the ability to suspend a hosting account at the panel, or to open a customer's control panel as that customer. Those are operational, and the people who take money are not automatically the people who run servers.

Reads and writes need different lines

Reading a connection's safe metadata — its name, its module, whether it is set up — is reasonable for any active member. Reading what a provider holds, which is another business's customer list, is not. Nor is minting a sign-in link into a customer's panel. Draw the line by consequence rather than by HTTP verb: a GET that returns somebody else's customer data is a privileged operation.

Credentials are never readable

An administrator can replace a provider credential. Nobody can read one back, including them. Store credentials outside the primary database where you can, record the act of setting one, and make rotation a normal operation rather than an incident. There is more in API credentials: least privilege and rotation.

Membership changes are events

  • Adding somebody is recorded: who added them, when, at what role.
  • Changing a role is recorded, with the previous role.
  • Removing somebody takes effect on their next request, not at their next login.
  • An invitation expires. An invitation that never expires is a credential sitting in somebody's email forever.
  • Ownership transfer is its own act with its own confirmation, and it is the one thing an administrator cannot do.

Where GENV is

GENV has five roles — owner, administrator, billing, support and read-only — with commercial and operational authority separated as above, and the tenancy boundary answers not-found for another workspace's records on every route. Provider credentials are held by a secrets store rather than in the application database and are never returned by any route. Membership changes and role changes are recorded acts.

Questions

Are five roles enough?

They are a sensible starting point for a small team. Per-permission configuration sounds flexible and can produce role sets nobody can audit. Add a role when you have a person who does not fit, not in advance.

Should support staff see invoices?

Reading them, usually yes — a support conversation is often about a bill. Changing them, no. That is the commercial axis.

How should contractors be handled?

As members with an expiring invitation and the narrowest role that works, reviewed on a schedule. The worst pattern is a shared login, because it makes every recorded act anonymous.