Running a managed security service is a balancing act that most vulnerability tools were never built to handle. You serve many clients at once. Each client expects that its data stays its own. Your analysts rotate across accounts, your reports carry different logos, and your auditors want proof that one tenant can never see another. A platform that assumes a single organization forces you to choose between two bad options. You either spin up a separate instance per client and lose every efficiency of scale, or you cram everyone into one flat workspace and pray nobody clicks the wrong filter.
PMAP is built for the first reality of an MSSP. The whole platform treats the tenant boundary as a first-class concept, not an afterthought bolted on at the report layer. This article walks through how an MSSP operations lead actually runs the business inside PMAP. It covers tenant isolation at the data layer, scoped role-based access control for rotating consultants, holding and subsidiary hierarchy, time-bound grants, per-client branding, and onboarding a new client without touching anyone else. Every claim here maps to how the product behaves, not to a wishlist.
If you want the broader category context first, read the pillar on multi-tenant vulnerability management. If you are weighing the operating model itself, the comparison piece on multi-tenant versus single-tenant MSSP tooling takes a different angle from this operator walkthrough.
The MSSP Problem: Many Tenants, Zero Crossover
The core promise an MSSP makes to every client is separation. Client A must never see a finding, an asset, a scan, or a report that belongs to Client B. That promise sounds simple until you consider how many surfaces it touches. A vulnerability list page filters records. A dashboard counts them. An analytics roll-up aggregates them. A search box queries them. A report job exports them. If isolation lives only in the user interface, a single missed filter on a single endpoint becomes a breach.
PMAP solves this at the root. The Company domain is the multi-tenancy root of the platform. Every tenant, whether it is a standalone client or one subsidiary inside a holding group, is a row in the companies table. Almost every other domain in the product treats company_id as its tenant key. Assets carry a company_id. Projects carry it. Scans, findings, campaigns, teams, reports, analytics, and dashboards all derive their multi-tenant scope from the company records.
That design matters because it moves the isolation boundary down to the data layer where it cannot be skipped. The user interface does not decide what an analyst can see. The query does. When an MSSP runs every client through one console, the question is never “did we remember to filter this screen.” The question is already answered before any screen renders.
One Console, Clean Tenant Boundaries
The mechanism that enforces this is the scope filter. When a user authenticates, the authz package builds a ScopeFilter from that user’s company membership. The filter carries an AllowedCompanyIDs slice. That slice contains exactly the company UUIDs the user belongs to. For a platform administrator who oversees the whole estate, the filter is marked Unrestricted and covers everything. For everyone else, it is a finite, explicit list.
Here is the part that makes the model trustworthy. Every other domain’s repository applies that filter as a SQL predicate. The query carries a WHERE company_id = ANY($n::uuid[]) clause. The company row becomes the effective tenant boundary across the entire platform. An analyst assigned to three clients sees data for those three companies and nothing else, because the database itself will not return rows outside the allowed set.
This is why a many-client console does not become a many-client liability. Your operations team works in one place. They switch context between clients without logging in and out of separate instances. The shared surface is genuine, but the data each person touches is bounded by their company membership at every list query. One console on top, clean tenant boundaries underneath.
Default-Deny by Design
The strongest isolation rule in the platform is the one that governs the empty case. A scope filter that is not Unrestricted and that carries an empty AllowedCompanyIDs is treated as default-deny. The repositories must return nothing.
This is the opposite of how a lot of systems fail. A naive filter treats an empty allow-list as “no constraint” and accidentally returns everything. PMAP treats an empty allow-list as “no access” and returns nothing. For an MSSP, this distinction is the difference between a contained mistake and a cross-tenant data exposure. If a grant is misconfigured, if a membership has not been set, or if something upstream produces an empty scope, the safe behavior is silence, not a full data dump. The platform errs toward showing too little, never too much.
Holding and Subsidiary Hierarchy
Many MSSP clients are not single legal entities. A client might be a holding group with several subsidiaries, each running its own assets and its own assessment cadence. PMAP models this directly through tenant hierarchy.
A company may carry an optional parent_id that points to its holding company. A company with parent_id set to null is a holding company, the root tenant. A company with a non-null parent_id is a subsidiary. The GetSubsidiaries capability lists all direct children under a holding company, and list and detail responses resolve a parent_name field through a self-join so the interface can show the parent organization without an extra round trip.
For an MSSP, this hierarchy is how you serve a corporate group cleanly. You onboard the holding company, then attach subsidiaries beneath it. The list view enriches each company with denormalized counts. You see asset count, location count, project count, and an open-finding severity summary per company, computed in the same query rather than fetched one row at a time. That enrichment is what makes a subsidiary roll-up usable at the operations desk. You glance at the group, you see where the open critical and high findings sit across subsidiaries, and you direct attention without opening ten tabs.
The platform also guards the hierarchy against obvious mistakes. A company cannot be its own parent. The service blocks any update where the parent reference equals the company’s own ID. A request to list subsidiaries for a parent that does not exist returns a not-found error rather than an empty success. The structure stays honest.
Scoped RBAC So Consultants See Only Their Client
Tenant isolation answers “which data exists for this user.” Scoped role-based access control answers “what can this user do, and within which boundary.” The RBAC domain is PMAP’s authorization backbone, and for an MSSP it is the difference between a consultant who can help on one engagement and a consultant who can wander the whole portfolio.
A user-access grant assigns a role to a user with a scope. The scope is one of three types. A global scope means the user is unrestricted across all tenants. A company scope means the user can access the specified company and all of its projects. A project scope means the user can access only the specified project.
The behavior that earns trust is what the project scope deliberately does not do. A project-scoped grant does not populate the allowed company set. A consultant scoped to a single project sees that project and no sibling projects at the same company. The platform documents the reasoning plainly. Adding the owning company to a project grant would let a project consultant see every sibling project at that company, which would be a cross-engagement data leak. PMAP refuses to make that inference.
For an MSSP this is exactly the control you need when you bring a specialist onto one client engagement. You grant them project scope. They see the work they were brought in for. They do not gain a window into the client’s other projects, and they certainly do not gain a window into your other clients. The narrow grant stays narrow.
The whole admin surface that manages roles and grants is itself locked down. The RBAC API is restricted to the platform_admin role through middleware applied to the entire admin route group. Defining roles and handing out access is an operator-level action, not something a tenant user can reach.
Custom Roles and the Permission Matrix
Off-the-shelf roles never quite fit a service business. You have senior consultants, junior testers, read-only client contacts, account leads, and reviewers. Each needs a different slice of capability. PMAP lets you define that slice explicitly.
A role’s permissions are expressed as a matrix of entity type by action. There are ten entity types: company, asset, project, finding, report, runbook, rule, integration, scan, and user. There are six actions: view, create, update, delete, approve, and export. Any combination of an entity and an action can be granted, which gives sixty possible permission pairs to compose into a role. A reviewer role might carry finding view, finding approve, and report view with export. A junior tester might carry finding view, finding create, and scan view. You build the role to match the job.
The role builder is not hardcoded. A permissions metadata endpoint returns the full catalog of entity types and actions, so the interface renders the matrix from the live catalog rather than a frozen list. When the platform adds capability, the builder reflects it without a front-end change.
System roles are protected. A role marked as a system role is seeded by migration and cannot be modified or deleted through the API. The platform defines platform_admin as a system role with wildcard authority. The protection is precise. A user-created role that happens to be named platform_admin does not inherit wildcard power, because the platform checks both the system flag and the name before short-circuiting to unrestricted access. You cannot accidentally or maliciously mint a god-mode role by copying a name.
When you change a role’s permissions, the platform purges the affected scope caches so the new permission set takes effect quickly rather than waiting out a cache lifetime. Editing a role does not leave stale authority lingering.
Time-Bound Access for Rotating Staff
MSSP staffing moves. A contractor joins for a six-week engagement. A partner firm gets temporary access for a joint assessment. An auditor needs to look at one client for the duration of a review. Permanent grants are wrong for all of these, because someone has to remember to revoke them, and the someone who forgets is how stale access becomes an audit finding.
PMAP supports time-bound grants directly. A user-access grant can carry an optional expires_at timestamp. When set, the grant is excluded from scope resolution the moment it lapses. The grant stops counting as soon as its expiry passes, not at the next administrative cleanup.
The enforcement detail is what makes this dependable rather than decorative. Scope resolution caches a user’s effective access for performance, but the cache lifetime is capped to the soonest expiry across that user’s grants. Time-bound access is revoked within seconds of expiry, not whenever a fixed cache window happens to roll over. A grant that expires at noon does not linger in a stale cache until the cache decides to refresh.
There is also an evidence trail for expiry. A background worker scans for grants that have lapsed but not yet been announced, emits an access_expired audit event for each, and marks the row so it is not announced twice. The worker is idempotent and safe across multiple server replicas, and it processes a bounded batch per run. For an MSSP, this means that when access ends, there is a recorded event saying so. You do not just trust that the grant expired. You can show that it did.
Assignment is also safe to repeat. The grant operation upserts on the combination of user, role, scope type, and scope. Re-granting the same role and scope updates the expiry rather than creating a duplicate row. Extending a contractor’s access by two weeks is a clean update, not a pile of overlapping grants you have to reconcile later.
Per-Client Branding and Reporting
A client report that carries your generic platform logo looks like a tool you bought. A client report that carries the client’s own brand looks like a service you deliver. The branding gap is small in pixels and large in perception.
PMAP attaches branding to the tenant. A company can carry its own logo, uploaded to object storage. The upload path is deliberately careful. The file size is capped, only image types are accepted, and the MIME type is sniffed from the actual bytes of the upload rather than trusted from the client-supplied header. The logo is retrieved as a streamed object with proper content-type and cache headers. Because the logo belongs to the company record, it follows the tenant wherever the tenant appears.
For an MSSP delivering per-client reporting, this is the foundation for output that looks like it was made for that client. The branding lives with the company, so reporting can present each tenant under its own identity rather than a one-size logo across the whole book of business. Your operations team works in one console, and each client still receives documents that look like their own.
Activation and Soft-Pause Per Client
Client relationships are not always on. An engagement pauses between contract phases. A client goes dormant for a quarter. A trial ends. You want to suspend activity for that tenant without deleting its history and without spilling its state into the rest of the platform.
PMAP gives each company an is_active flag that soft-pauses the tenant. The enforcement runs through an activation gate. Sibling services for assets, scans, projects, and campaigns call an EnsureActive check before they create new resources under a tenant. When a company is inactive, that gate blocks new resource creation under it. The tenant’s existing data stays in place, but the platform stops accepting fresh work for it.
There is one deliberate exception that matters for an MSSP. Scanner integrations bypass the activation gate so automated ingestion pipelines keep running even while a tenant is administratively paused. The reasoning is operational. You may pause new manual activity on a dormant client while still letting scheduled scan data flow in, so that when the relationship resumes you are not starting from a stale picture. The pause is about administrative resource creation, not a hard stop on every signal.
This soft-pause model lets you manage the lifecycle of a client relationship inside the same console you use to run it. You do not archive and restore. You flip a flag, the gate takes effect, and the tenant’s history remains intact and isolated.
Onboarding a New Client Without Disrupting Others
The test of a multi-tenant platform is the moment you add the next tenant. In a flat or single-tenant system, onboarding a new client means standing up a new instance, or it means editing shared configuration that risks the clients you already serve. Neither is acceptable when you are signing clients regularly.
In PMAP, creating a tenant is a contained, operator-controlled action. Company creation is restricted to platform administrators. The platform enforces this at the handler layer, which is intentionally stricter than the general role-matrix grant for company creation. Adding a tenant row is an operator decision, and the platform treats it that way.
Once the company exists, you give it shape. You can attach it under a holding parent if it belongs to a group. You manage its physical locations, each carrying a name and an address and its own active flag. Under each location, you register IP CIDR scopes. Duplicate CIDRs within the same location are rejected, so the scope list stays clean. Those IP scopes are not decoration. Scanner integrations use them to correlate discovered assets back to the right tenant, which is how automated ingestion knows which company a freshly seen host belongs to.
The key property is containment. A new company is a new row. Its scope is its own. Because every downstream query is bounded by the allowed company set, adding Client N has no effect on what the analysts serving Clients 1 through N-minus-1 can see. There is no shared instance to disturb and no global list to edit. You onboard, you scope, you begin. The existing tenants never notice.
How PMAP Scales to Many Clients
Step back and the pattern is consistent. The company row is the tenant boundary, and that boundary is enforced in the data layer through the allowed company set applied as a SQL predicate on every list query. The default for an unscoped, restricted user is deny, so mistakes contain themselves. The RBAC layer adds the second dimension, where global, company, and project scopes decide reach, and a project scope never silently opens the company it belongs to.
On top of that foundation sit the operational features an MSSP runs every day. Holding and subsidiary hierarchy with subsidiary roll-up. Custom roles built from a sixty-pair permission matrix, with system roles protected from tampering. Time-bound grants for rotating staff, revoked within seconds of expiry and recorded with an audit event. Per-client branding carried on the company record. Soft-pause activation that suspends a tenant without losing its history. Operator-gated onboarding that adds a client without touching the rest.
None of these are stitched together at the surface. They share the same tenant key, so they reinforce each other instead of leaving gaps between them. That is what lets a managed security provider run many clients from one console without trading away the isolation those clients are paying for. The console is shared. The tenants are not.
If you want the architectural context behind this model, the multi-tenant vulnerability management pillar covers the foundation in depth. If you are still deciding between operating models, the multi-tenant versus single-tenant MSSP tooling comparison sets the trade-offs side by side. For external grounding, the NIST RBAC model (INCITS 359) and NIST SP 800-207 Zero Trust Architecture describe the access-control principles this design implements, and CIS Controls v8 Control 6 frames access control management as a security baseline.
Read the identity and multi-tenancy datasheet and run every client from one console.