Platform
Multi-tenant permissions for hosting teams
Who can suspend a customer, who can issue a refund and who can read a credential are three questions. Collapsing them into admin-or-not is the usual mistake.
10 min read · Reviewed 2026-09-21
Issue a separate credential per integration with the narrowest privileges that work, keep the value outside your application database, never display it again after it is set, and rehearse rotation before you need it. A credential nobody can rotate calmly is one that will be rotated during an incident.
The credentials a billing platform holds are among the most powerful in a hosting business: a WHM token can terminate accounts, a registrar key can transfer domains away. They deserve more care than they usually get, and OWASP's secrets management guidance is a reasonable standard to hold yourself to.
Test the scoping by attempting something the credential should not be able to do. Reading the permissions screen tells you what you configured; attempting the action tells you what is true.
Not in your application database alongside the business data. A secrets store keeps the value, and the application keeps a reference plus a generation number. Two properties follow, and both matter:
The second property is the one that changes day-to-day behaviour. A system that can say "the store answered and does not have this version" can tell a person to re-enter it. One that cannot will report a generic provider failure and send somebody looking at the wrong thing.
Once set, a credential is write-only from the application's point of view. No route returns it, no screen displays it, no log records it, no export includes it. An administrator may replace it; nobody may read it. If your system has a "reveal" button, the credential is effectively readable by anybody who compromises an administrator session.
Rotation is the operation that proves the design. The sequence that works:
The step people miss is the fourth. A queued operation that resolved its credential before the rotation may still be running; revoking immediately turns it into a failure of exactly the ambiguous kind that is expensive to resolve.
| Event | Recorded | Not recorded |
|---|---|---|
| Credential set | Who, when, which connection, which generation | The value |
| Credential rotated | Who, when, previous generation | Either value |
| Connection checked | Outcome, whether a real provider answered | The credential, the provider's raw response |
| Provider refused | That it refused, in your own words | The provider's message verbatim |
The last row is worth a sentence. Provider error text is not yours: it can contain identifiers from other customers, internal hostnames, or wording you do not want to show. Map to your own closed vocabulary and record that.
GENV holds provider credentials in a secrets store outside the application database, records a reference and a generation rather than a value, and returns credentials from no route at all. Each call resolves one under the narrowest scope the module declares — which bounds what GENV asks for and not what the token can do at the provider, since that is set where you issued it. A connection check records whether a real provider answered, which in the current build is always no, because every provider module runs against a deterministic stand-in. The distinction between "the store could not be reached" and "the store answered and no longer has this version" is represented explicitly, because they need different responses from a person.
On a schedule you can actually keep, plus immediately when somebody with access leaves. Quarterly is a reasonable default; the important part is having rehearsed it so it is uneventful.
It is better than plaintext and it is weaker than a separate store, because the key is usually available to the same application that was compromised. It also cannot represent "the store no longer has this".
Acceptable for the application's own infrastructure credentials, poor for per-customer provider credentials, which need to be added and rotated without a deployment.
Official documentation, each requested on 21 September 2026. Provider behaviour changes; check the source before relying on a detail.