Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions docs/examples/multitenancy/operator-values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
global:
imagePullSecrets: [{name: nvcr-image-pull-secret}]
agentSandbox:
install: false
llm:
mode: external
external:
baseUrl: https://llm.example.com/v1
model: served-model
existingSecret: mosaic-inference
mosaicUi:
auth:
existingSecret: mosaic-ui-auth
modules:
ui: {enabled: true}
execution: {enabled: true}
kubernetes: {enabled: true}
observability: {enabled: true}
grafana: {enabled: true}
bcm: {enabled: true}
slurm: {enabled: false}
diagnostics: {enabled: false}
research: {enabled: false}
terminal: {enabled: false}
clusters: {enabled: true}
edit: {enabled: false}
kubernetes:
defaultCluster: local
local:
enabled: true
observability:
prometheusUrl: http://prometheus.monitoring.svc.cluster.local:9090
alertmanagerUrl: http://alertmanager.monitoring.svc.cluster.local:9093
grafanaUrl: https://grafana.example.com/grafana
grafanaDatasourceUid: prometheus
grafanaAuth:
existingSecret: mosaic-grafana-auth
bcmMcp:
enabled: true
mode: ssh-adapter
headHost: operator@bcm-head.example.com
hostSshKeySecretName: bcm-host-ssh-key
33 changes: 33 additions & 0 deletions docs/examples/multitenancy/tenant-reader.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: mosaic-job-reader
namespace: tenant-a
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: mosaic-job-reader
namespace: tenant-a
rules:
- apiGroups: [""]
resources: [pods, pods/log, events]
verbs: [get, list, watch]
- apiGroups: [batch]
resources: [jobs, cronjobs]
verbs: [get, list, watch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mosaic-job-reader
namespace: tenant-a
subjects:
- kind: ServiceAccount
name: mosaic-job-reader
namespace: tenant-a
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: mosaic-job-reader
37 changes: 37 additions & 0 deletions docs/examples/multitenancy/tenant-values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
global:
imagePullSecrets: [{name: nvcr-image-pull-secret}]
agentSandbox:
install: false
llm:
mode: external
external:
baseUrl: https://llm.example.com/v1
model: served-model
existingSecret: mosaic-inference
mosaicUi:
auth:
existingSecret: mosaic-ui-auth
modules:
ui: {enabled: true}
execution: {enabled: true}
kubernetes: {enabled: true}
observability: {enabled: false}
grafana: {enabled: false}
bcm: {enabled: false}
slurm: {enabled: false}
diagnostics: {enabled: false}
research: {enabled: false}
terminal: {enabled: false}
clusters: {enabled: false}
edit: {enabled: false}
bcmMcp:
enabled: false
kubernetes:
defaultCluster: tenant-a
local:
enabled: false
clusters:
- name: tenant-a
kubeconfigSecretRef:
name: tenant-a-kubeconfig
key: config
31 changes: 31 additions & 0 deletions docs/images/tenant-operator-instances.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions docs/multitenancy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Tenant and operator deployments

Deploy separate Mosaic instances for operators and tenants. **Operators focus on hardware and systems. Tenants focus on their jobs.** Each instance is a separate Helm release with its own UI login, agent state, audit storage, and credentials.

![Separate operator and tenant Mosaic instances, each with its own login, state, and credentials. The operator accesses cluster infrastructure; the tenant accesses only its jobs.](images/tenant-operator-instances.svg)

| Instance | Focus | Example question |
| --- | --- | --- |
| Operator | Cluster health, hardware inventory, networking, and system telemetry | “Which nodes are unhealthy?” |
| Tenant | Its own jobs, Pods, events, and logs | “Why is my job pending?” |

Repeat the tenant instance for each tenant. In an enterprise, these audiences can be the infrastructure team and an application team. Within a tenant VM, visibility is limited to resources exposed to that VM or its scoped APIs.

## Example setup

These Kubernetes examples are read-only. The provider installs both releases and reuses an existing compatible agent-sandbox controller.

- **Tenant:** [tenant-values.yaml](examples/multitenancy/tenant-values.yaml) disables infrastructure integrations and the default cluster reader. Apply [tenant-reader.yaml](examples/multitenancy/tenant-reader.yaml) in workload namespace `tenant-a`, provision a kubeconfig for that identity, and store it as Secret `tenant-a-kubeconfig` (key `config`) in `mosaic-tenant-a`.
- **Operator:** [operator-values.yaml](examples/multitenancy/operator-values.yaml) enables cluster monitoring, observability, and [Base Command Manager](bcm.md). Configure the monitoring URLs, `mosaic-grafana-auth` Secret (`username`, `password`), and `bcm-host-ssh-key` Secret (`id_ecdsa`) for its read-only SSH identity in `mosaic-operator`.

Complete [namespace and registry access setup](installation.md#1-create-the-namespace-and-registry-access) for each release. In each namespace, create a distinct `mosaic-ui-auth` Secret (`username`, `password`, `machineToken`) and `mosaic-inference` Secret (`apiKey`); replace the example inference URL and model with your approved endpoint.

Install with your approved chart version:

```bash
helm upgrade --install mosaic-tenant-a \
oci://nvcr.io/0948643769302270/afoa-release/mosaic-stack \
--version "$MOSAIC_CHART_VERSION" --namespace mosaic-tenant-a \
-f tenant-values.yaml --atomic --wait --timeout 12m

helm upgrade --install mosaic-operator \
oci://nvcr.io/0948643769302270/afoa-release/mosaic-stack \
--version "$MOSAIC_CHART_VERSION" --namespace mosaic-operator \
-f operator-values.yaml --atomic --wait --timeout 12m
```

## Access boundaries

Backend credentials and Kubernetes RBAC enforce access. **The chart's default Kubernetes reader is cluster-wide**, even when installed in a tenant namespace; keep it disabled for tenants. Before handover, verify with the tenant kubeconfig that job and log reads work in `tenant-a`, while other namespaces, nodes, Secrets, and job mutations are denied.

Give each audience its own UI address and login. Enforce network isolation with authenticated ingress and provider-managed network policies; namespace separation alone is insufficient. Keep storage and credentials separate, including any added MCP or metrics backend. These are separate deployments, not per-user authorization in a shared UI. Cluster administrators and the runtime controller remain trusted; use separately administered clusters or VMs when required for mutually untrusted tenants.
1 change: 1 addition & 0 deletions index.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Kind installation <docs/kind_installation>
:hidden:

Security model <docs/security_model>
Tenant and operator deployments <docs/multitenancy>
Use cases <docs/use_cases>
Slurm root cause analysis <docs/slurm_rca>
Headless integration <docs/skills/ai-factory-operations-agent-headless/SKILL>
Expand Down
Loading