Skip to content
ALTAIRA LABS
DocsBlog

Manage workspaces

This guide covers creating workspaces, configuring access control, and setting network isolation for multi-tenant deployments.

  • Omnia operator deployed with Workspace controller enabled
  • kubectl access to the cluster
  • (For production) Identity provider configured for OIDC

A workspace provides an isolated environment for a team with its own namespace, RBAC, and network isolation.

Create a minimal workspace:

apiVersion: omnia.altairalabs.ai/v1alpha1
kind: Workspace
metadata:
name: my-team
spec:
displayName: "My Team"
namespace:
name: omnia-my-team
create: true

Apply it:

Terminal window
kubectl apply -f workspace.yaml

The controller will:

  1. Create the namespace omnia-my-team
  2. Create ServiceAccounts for each role (owner, editor, viewer)
  3. Set up RBAC bindings
Terminal window
kubectl get workspace my-team -o yaml

Check the status section:

status:
phase: Ready
namespace:
name: omnia-my-team
serviceAccounts:
owner: workspace-my-team-owner-sa
editor: workspace-my-team-editor-sa
viewer: workspace-my-team-viewer-sa

Map identity provider groups to workspace roles:

apiVersion: omnia.altairalabs.ai/v1alpha1
kind: Workspace
metadata:
name: customer-support
spec:
displayName: "Customer Support Team"
namespace:
name: omnia-customer-support
create: true
roleBindings:
# Team leads get full control
- groups:
- "cs-admins@acme.com"
role: owner
# Engineers can create and modify resources
- groups:
- "cs-engineers@acme.com"
- "support-team"
role: editor
# Contractors get read-only access
- groups:
- "cs-contractors@acme.com"
role: viewer

When users authenticate via OIDC, their group claims are matched against these bindings.

Grant access to ServiceAccounts for automated pipelines:

spec:
roleBindings:
# ArgoCD can deploy agents
- serviceAccounts:
- name: argocd-application-controller
namespace: argocd
role: editor
# GitHub Actions can deploy
- serviceAccounts:
- name: github-actions
namespace: ci-system
role: editor

For exceptions (use sparingly):

spec:
directGrants:
# Temporary admin access for incident response
- user: oncall@acme.com
role: owner
expires: "2026-02-01T00:00:00Z"

For development environments without authentication:

spec:
anonymousAccess:
enabled: true
role: viewer # Read-only for anonymous users

Until workspace-native quotas land, apply a standard Kubernetes ResourceQuota directly to the workspace namespace. The controller creates the namespace for you (see above), so you can quota it like any other namespace:

apiVersion: v1
kind: ResourceQuota
metadata:
name: workspace-limits
namespace: omnia-customer-support # Workspace namespace
spec:
hard:
requests.cpu: "50"
requests.memory: "100Gi"
limits.cpu: "100"
limits.memory: "200Gi"
configmaps: "100"
secrets: "50"
persistentvolumeclaims: "20"
Terminal window
kubectl apply -f resourcequota.yaml
kubectl describe resourcequota workspace-limits -n omnia-customer-support

Classify workspaces by environment:

spec:
environment: production # development | staging | production

This enables environment-based policies and promotion workflows.

Add tags for cost tracking:

spec:
defaultTags:
team: "customer-support"
cost-center: "CC-1234"
business-unit: "support-ops"

These tags are applied to all resources created in the workspace.

Network isolation restricts traffic to and from your workspace namespace using Kubernetes NetworkPolicies. This provides defense-in-depth for multi-tenant environments.

Add network isolation to restrict traffic:

spec:
networkPolicy:
isolate: true

This automatically creates a NetworkPolicy that:

  • Allows DNS queries to kube-system
  • Allows all traffic within the workspace namespace
  • Allows traffic to/from namespaces labeled omnia.altairalabs.ai/shared: true
  • Allows egress to external IPs (for LLM APIs) but blocks other private IP ranges

Check that the NetworkPolicy was created:

Terminal window
kubectl get networkpolicy -n omnia-customer-support

You should see:

NAME POD-SELECTOR AGE
workspace-customer-support-isolation <none> 1m

If agents need to receive traffic from an ingress controller:

spec:
networkPolicy:
isolate: true
allowFrom:
- peers:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx

To allow agents to connect to internal databases or services:

spec:
networkPolicy:
isolate: true
allowTo:
- peers:
- ipBlock:
cidr: 10.0.0.0/8 # Internal network
ports:
- protocol: TCP
port: 5432 # PostgreSQL
- protocol: TCP
port: 6379 # Redis

For high-security environments, disable external API access:

spec:
networkPolicy:
isolate: true
allowExternalAPIs: false
allowTo:
# Only allow specific external endpoints
- peers:
- ipBlock:
cidr: 104.18.0.0/16 # Example: specific API provider
ports:
- protocol: TCP
port: 443

Allow private networks (local development)

Section titled “Allow private networks (local development)”

For local development or when agents need to access services on private networks (e.g., a local Ollama instance):

spec:
networkPolicy:
isolate: true
allowPrivateNetworks: true

This removes the RFC 1918 private IP exclusions (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), allowing agents to reach services on your local network.

To remove network restrictions, either delete the networkPolicy section or set isolate: false:

spec:
networkPolicy:
isolate: false

The controller will automatically delete the NetworkPolicy.

A workspace’s namespace and RBAC don’t give its agents anywhere to persist conversations or memory — that comes from a service group (spec.services[]), which provisions the session-api and memory-api the agents use.

Setting this up (database secrets, managed vs external mode, pointing agents at a group) is covered in its own guide: Configure workspace service groups.

Once your workspace is ready — and its service group is provisioned — deploy agents to its namespace:

apiVersion: omnia.altairalabs.ai/v1alpha1
kind: AgentRuntime
metadata:
name: support-bot
namespace: omnia-customer-support # Workspace namespace
spec:
serviceGroup: default # session/memory backend (see service groups guide)
promptPackRef:
name: support-prompts
providers:
- name: default
providerRef:
name: claude-provider

The dashboard automatically scopes resources to the current workspace.

The dashboard shows a workspace selector in the header. Users only see workspaces they have access to.

When you select a workspace, the dashboard shows:

  • Agents deployed in that workspace
  • PromptPacks in the workspace namespace
  • Events and logs scoped to that workspace

The dashboard enforces role-based access:

Role Can View Can Create/Edit Can Delete Can Manage Members
viewer Yes No No No
editor Yes Yes Yes No
owner Yes Yes Yes Yes

A production-ready workspace with all features:

apiVersion: omnia.altairalabs.ai/v1alpha1
kind: Workspace
metadata:
name: customer-support
spec:
displayName: "Customer Support Team"
description: "Team responsible for customer support AI agents"
environment: production
defaultTags:
team: "customer-support"
cost-center: "CC-1234"
namespace:
name: omnia-customer-support
create: true
labels:
environment: production
team: customer-support
roleBindings:
- groups:
- "cs-admins@acme.com"
role: owner
- groups:
- "cs-engineers@acme.com"
role: editor
- groups:
- "cs-contractors@acme.com"
role: viewer
- serviceAccounts:
- name: argocd-application-controller
namespace: argocd
role: editor
networkPolicy:
isolate: true
allowFrom:
- peers:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
allowTo:
- peers:
- ipBlock:
cidr: 10.0.0.0/8
ports:
- protocol: TCP
port: 5432

Symptom: Workspace phase remains Pending

Check:

  1. Verify namespace doesn’t already exist with conflicting labels
  2. Check operator logs: kubectl logs -n omnia-system deploy/omnia-controller-manager
  3. Ensure spec.namespace.create: true if namespace should be auto-created

Symptom: User can’t access workspace in dashboard

Check:

  1. Verify user’s groups in JWT token (decode at jwt.io)
  2. Confirm group names match exactly in roleBindings
  3. Check if anonymous access is enabled (for development)

Symptom: API calls fail with authentication errors

Check:

  1. Verify ServiceAccounts exist: kubectl get sa -n omnia-customer-support
  2. Check RoleBindings: kubectl get rolebindings -n omnia-customer-support
  3. Ensure workspace phase is Ready

Symptom: Cannot create new resources

Quotas are not managed by the Workspace controller; if you applied a native ResourceQuota to the namespace (see Limit resource usage):

Check:

  1. View current usage: kubectl describe resourcequota -n omnia-customer-support
  2. Review the ResourceQuota spec.hard limits
  3. Clean up unused resources or raise the limits

Symptom: Agents can’t reach external APIs or internal services

Check:

  1. Verify NetworkPolicy exists: kubectl get networkpolicy -n omnia-customer-support
  2. Check if allowExternalAPIs: false is blocking external traffic
  3. Inspect the NetworkPolicy rules: kubectl describe networkpolicy workspace-customer-support-isolation -n omnia-customer-support
  4. Add custom allowTo rules for required services

Debug with a test pod:

Terminal window
kubectl run -n omnia-customer-support debug --rm -it --image=busybox -- sh
# Inside the pod:
nslookup api.anthropic.com # Test DNS
wget -qO- https://api.anthropic.com # Test external access

Symptom: Ingress traffic doesn’t reach agents

Check:

  1. Ensure ingress controller namespace is allowed in allowFrom:
    allowFrom:
    - peers:
    - namespaceSelector:
    matchLabels:
    kubernetes.io/metadata.name: ingress-nginx
  2. Verify the ingress controller namespace has the correct labels
  3. Check that the NetworkPolicy allows the required ports