Two gaps found by exercising the MSP pilot path live on a throwaway multi-tenant instance: 1. CheckAccess granted any authenticated principal access to the default org, so a token bound to a client org could read the provider's own default-org estate if it leaked from a client site. Org-bound tokens now fall through to the explicit binding check for the default org; authenticated users and legacy unbound tokens keep default-org access, and binding "default" explicitly still grants it. 2. The webhook private-target allowlist (instance-wide system setting) only ever reached the default org's notification manager on startup/reload, and only the request-context org on settings update. Tenant orgs' webhooks to private targets (per-client Gotify over VPN, the canonical MSP alert route) failed SSRF validation with no org-side remedy, and any allowlist died with a restart. Settings updates and reloads now fan out to every live tenant manager via the new MultiTenantMonitor.ForEachMonitor, and tenant monitors inherit the persisted allowlist and public URL at creation. Both fixes verified live: org-bound token vs default org returns 403; client-org webhooks to a private target succeed after restart and for orgs created after the allowlist was saved. MSP.md validation checklist gains the default-org probe and the allowlist guidance; MULTI_TENANT.md documents the binding semantics. Contracts updated for api-contracts, security-privacy, and monitoring with adjacency notes for agent-lifecycle, storage-recovery, and performance-and-scalability.
9.3 KiB
Multi-Tenant Organizations (Enterprise/Internal)
Pulse supports shared-process organizations for Enterprise and internal multi-organization deployments. Each organization gets its own infrastructure, resources, alerts, and audit log namespace on the same Pulse process.
This is not the canonical Pulse MSP model for separate customer businesses. MSP crosses legal and security ownership boundaries, so the canonical MSP route is provider-hosted: a Stripe-free provider control plane runs one isolated Pulse runtime per client workspace. Use shared-process organizations when one owner is deliberately separating internal sites, teams, departments, or environments.
Requirements
| Requirement | Detail |
|---|---|
| Feature flag | PULSE_MULTI_TENANT_ENABLED=true |
| License | Enterprise license with multi_tenant capability |
Without these, all API calls return 501 Not Implemented (flag off) or 402 Payment Required (no license). The default organization always works regardless.
Quick Start
- Set
PULSE_MULTI_TENANT_ENABLED=truein your environment and restart Pulse. - Activate your Enterprise license in Settings → Plans.
- Go to Settings → Organization and click Create Organization.
- Name your organization and assign infrastructure to it.
- Use the Org Switcher in the header bar to switch between organizations.
Concepts
Organizations
An organization is a separate monitoring namespace inside the same Pulse runtime:
- Its own set of monitored nodes and resources.
- Its own alerts, thresholds, and notifications.
- Its own audit log.
- Its own configuration directory on disk.
The default organization always exists and is used when multi-tenant is disabled. It cannot be deleted or renamed.
Roles
Each member has a role within an organization:
| Role | Permissions |
|---|---|
| Owner | Full control. Can transfer ownership, delete the org. |
| Admin | Manage members, shares, and org settings. Cannot transfer ownership. |
| Editor | Read/write access to org resources. Cannot manage members or shares. |
| Viewer | Read-only access to all org data. |
Resource Sharing
Organizations can share specific resources with other organizations:
- Share a VM, container, host, or storage resource with another org.
- Assign an access role (
viewer,editor, oradmin) to the share. - The receiving org sees shared resources alongside their own, with a share badge.
Managing Organizations
Creating an Organization
UI: Settings → Organization → Create Organization
API:
curl -X POST http://localhost:7655/api/orgs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Production Datacenter", "description": "EU production infrastructure"}'
Switching Organizations
Use the Org Switcher dropdown in the header. When you switch:
- All pages reload with the new organization's data.
- AI chat history is reset (each org has its own context).
- Caches are invalidated and re-fetched.
Managing Members
UI: Settings → Organization → Access
API:
# List members
curl http://localhost:7655/api/orgs/{orgId}/members \
-H "Authorization: Bearer $TOKEN"
# Add a member
curl -X POST http://localhost:7655/api/orgs/{orgId}/members \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"userId": "user-id", "role": "editor"}'
# Update role
curl -X PATCH http://localhost:7655/api/orgs/{orgId}/members/{userId} \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "admin"}'
Sharing Resources
UI: Settings → Organization → Sharing
API:
# Create a share
curl -X POST http://localhost:7655/api/orgs/{orgId}/shares \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"targetOrgId": "other-org-id",
"resourceType": "host",
"resourceId": "resource-id",
"role": "viewer"
}'
# View incoming shares
curl http://localhost:7655/api/orgs/{orgId}/shares/incoming \
-H "Authorization: Bearer $TOKEN"
Monitoring Multiple Internal Estates
An Enterprise deployment can run one central Pulse server and keep each internal estate in its own organization, so dashboards, alerts, notifications, and audit logs are scoped by organization. The same default node names (pve, pve1) in different organizations do not collide, because each organization is a separate namespace.
Use this for one company operating many internal sites, teams, departments, or environments. Do not use this as the default MSP model for unrelated customer businesses; MSP client isolation belongs to the provider-hosted client-workspace model with one isolated Pulse runtime per client.
To onboard an internal estate:
- Create an organization for the estate (see Creating an Organization).
- Create an org-bound API token with the
agent:reportscope, bound to that estate's organization (orgId). A token bound to a single organization automatically routes every agent that uses it into that organization, with no extra header required. Binding also scopes the token: an org-bound token cannot access other organizations, including the default org (binddefaultexplicitly if a token genuinely needs it). Legacy unbound tokens keep their default-org access. - Install the estate's agents (Proxmox host, Docker, Kubernetes) using that token. Their telemetry lands in the selected organization.
- (Optional) Alias node names per estate. If two estates both use the default
pvehostname and you want them visually distinct, set--hostname(or thePULSE_HOSTNAMEenvironment variable) on the agent, for example--hostname "acme-pve1". See UNIFIED_AGENT.md. - (Optional) Isolate agent check-in on its own port. When remote nodes reach the central server across the internet, enable Split-Port Agent Ingest so agents connect on a dedicated, firewalled port that exposes only
/api/agents/*and never the web UI or management API.
Route each estate's alerts into the right internal system with per-organization webhooks or the org-scoped alerts API. See the multi-tenant section of WEBHOOKS.md.
Licensing: self-hosted multi-tenant requires an Enterprise license with the multi_tenant capability (see Requirements). MSP licensing is separate and is based on a signed provider MSP license that sets the client workspace cap for isolated client runtimes, not shared-process organizations.
Settings Panels
When multi-tenant is enabled, Settings → Organization shows:
| Panel | Description |
|---|---|
| Overview | Organization name, description, creation date |
| Access | Member list, invite/remove members, change roles |
| Sharing | Outgoing and incoming resource shares |
| Billing & Plan | Organization-level plan and license info |
API Reference
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/orgs |
List organizations the current user can access |
POST |
/api/orgs |
Create a new organization |
GET |
/api/orgs/{id} |
Get organization details |
PATCH |
/api/orgs/{id} |
Update organization |
DELETE |
/api/orgs/{id} |
Delete organization |
GET |
/api/orgs/{id}/members |
List members |
POST |
/api/orgs/{id}/members |
Add a member |
PATCH |
/api/orgs/{id}/members/{userId} |
Update member role |
DELETE |
/api/orgs/{id}/members/{userId} |
Remove a member |
GET |
/api/orgs/{id}/shares |
List outgoing shares |
GET |
/api/orgs/{id}/shares/incoming |
List incoming shares |
POST |
/api/orgs/{id}/shares |
Create a share |
DELETE |
/api/orgs/{id}/shares/{shareId} |
Remove a share |
Tenant Context
All data-fetching endpoints respect the active organization context. The active org is determined by:
X-Pulse-Org-IDheader (API clients)- Session cookie (browser)
- Falls back to the
defaultorganization
Storage
- The default org uses the root data directory (backward compatible).
- Non-default orgs store data in
{data-dir}/orgs/{org-id}/. - Organization metadata is stored in
org.jsoninside each org directory. - When multi-tenant is first enabled, legacy single-tenant data is migrated into
orgs/default/with symlinks for compatibility.
Troubleshooting
"Multi-tenant is not enabled on this server" (501)
Set PULSE_MULTI_TENANT_ENABLED=true in your environment and restart Pulse.
"Multi-tenant requires an Enterprise license" (402)
Activate an Enterprise license with the multi_tenant capability in Settings → Plans.
Organization data not loading after switch
- Hard-refresh the browser (
Ctrl+Shift+R). - Check the Org Switcher dropdown — ensure the correct org is selected.
- Check Pulse logs for tenant middleware errors.
Shared resources not appearing
- Verify the share exists: Settings → Organization → Sharing → Incoming.
- Confirm the share role grants sufficient access.
- Check that the source org's resources are online.
See Also
- Plans & Entitlements, multi-tenant availability by plan
- Pulse Cloud, hosted Pulse environment
- Security, authentication and authorization model