# Multi-Tenancy Patterns in Provisioning

Implement secure multi-tenancy with data isolation, billing, and resource limits.

## Multi-Tenancy Architecture

```text
Central Control Plane
├─ Tenant Management
├─ Billing Engine
├─ Audit Logging
└─ Policy Enforcement
    ↓
Tenant 1              Tenant 2              Tenant 3
├─ Workspace A        ├─ Workspace C        ├─ Workspace E
├─ Workspace B        └─ Workspace D        └─ Workspace F
└─ Isolated Storage   └─ Isolated Storage   └─ Isolated Storage
```

## Tenant Configuration

```nickel
{
  tenants = [
    {
      id = "tenant-001",
      name = "acme-corp",
      tier = "enterprise",

      limits = {
        max_workspaces = 50,
        max_compute_nodes = 1000,
        max_storage_gb = 10000,
        monthly_cost_limit = 100000
      },

      isolation = {
        network_isolation = "vpc-per-tenant",
        database_isolation = "schema-per-tenant",
        key_isolation = "cmk-per-tenant",
        audit_log_isolation = "dedicated-stream"
      },

      billing = {
        model = "usage",
        payment_method = "credit-card",
        billing_contact = "[billing@acme.com](mailto:billing@acme.com)",
        budget_alerts = true
      },

      features = [
        "batch-workflows",
        "multi-cloud",
        "custom-providers",
        "sso",
        "audit-logging",
        "custom-domains"
      ]
    }
  ]
}
```

## Workspace Isolation

Each tenant has isolated workspaces:

```nushell
def create-tenant-workspace [--tenant: string, --name: string] {
    print $"Creating workspace for tenant ($tenant)..."

    # 1. Create isolated VPC
    let vpc_id = (
        provisioning vpc create \
            --name $"($tenant)-($name)" \
            --cidr "10.0.0.0/16" \
            --owner-tag $tenant
    )

    # 2. Create isolated database schema
    let schema_name = $"tenant_($tenant)_($name)"
    provisioning database schema create \
        --name $schema_name \
        --owner $tenant

    # 3. Create CMK for encryption
    let cmk = (
        provisioning kms create-key \
            --key-spec AES-256 \
            --owner-tag $tenant
    )

    # 4. Create workspace
    let workspace = (
        provisioning workspace create \
            --name $name \
            --tenant-id $tenant \
            --vpc-id $vpc_id \
            --database-schema $schema_name \
            --encryption-key-id $cmk.key_id
    )

    # 5. Configure isolation policies
    provisioning policy attach \
        --workspace $workspace.id \
        --policy "tenant-isolation-$tenant"

    print $"✓ Workspace created with isolated resources"
}
```

## Data Isolation

### Database Schema Isolation

```sql
-- PostgreSQL: Schema-per-tenant
CREATE SCHEMA tenant_acme_workspace1;
CREATE SCHEMA tenant_globex_workspace1;

-- Row-level security for additional isolation
CREATE POLICY tenant_isolation ON infrastructure
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

-- Application sets tenant context per request
SET app.tenant_id = 'tenant-001';
SELECT * FROM infrastructure;  -- Only returns tenant-001's data
```

### Encryption Key Isolation

```rust
pub async fn encrypt_for_tenant(
    tenant_id: &str,
    plaintext: &[u8],
) -> Result<Vec<u8>> {
    // Get tenant-specific CMK
    let cmk = kms_client
        .get_key(tenant_id)
        .await?;

    // Encrypt with tenant's key
    let ciphertext = crypto::encrypt(plaintext, &cmk)?;

    Ok(ciphertext)
}
```

## Resource Limits per Tenant

```nushell
def enforce-tenant-limits [--tenant: string] {
    let limits = (provisioning tenant get --id $tenant | get limits)
    let current = (provisioning resource analyze --tenant $tenant)

    # Check compute limit
    if $current.total_compute > $limits.max_compute_nodes {
        print "⚠️  Tenant has exceeded compute limit"
        provisioning tenant enforce-limit \
            --tenant $tenant \
            --resource compute \
            --action block-new-deployments
    }

    # Check storage limit
    if $current.total_storage > $limits.max_storage_gb {
        print "⚠️  Tenant has exceeded storage limit"
        provisioning tenant enforce-limit \
            --tenant $tenant \
            --resource storage \
            --action increase-cost-or-block
    }

    # Check cost limit
    let monthly_cost = (provisioning cost current-month --tenant $tenant)
    if $monthly_cost.total > $limits.monthly_cost_limit {
        print "⚠️  Tenant has exceeded budget"
        provisioning tenant enforce-limit \
            --tenant $tenant \
            --resource budget \
            --action notify-admin
    }
}
```

## Multi-Tenancy Billing

```nushell
def calculate-tenant-invoice [--tenant: string, --month: string] {
    print $"Calculating invoice for ($tenant) - ($month)..."

    let usage = (
        provisioning billing analyze \
            --tenant $tenant \
            --month $month
    )

    let invoice = {
        tenant_id: $tenant,
        invoice_period: $month,
        summary: {
            compute_cost: $usage.compute_cost,
            storage_cost: $usage.storage_cost,
            network_cost: $usage.network_cost,
            database_cost: $usage.database_cost,
            other_cost: $usage.other_cost,
            subtotal: $usage.total_cost
        },
        charges: {
            usage_cost: $usage.total_cost,
            support_surcharge: ($usage.total_cost * 0.05),
            discount_applied: ($usage.total_cost * -0.10)
        },
        line_items: (format-line-items --usage $usage)
    }

    $invoice
    | update total = ($invoice.summary.subtotal + $invoice.charges.support_surcharge + $invoice.charges.discount_applied)
}
```

## Audit and Compliance

Multi-tenant audit trail with tenant isolation:

```nushell
def audit-tenant-activity [--tenant: string, --days: int = 30] {
    print $"Auditing activity for ($tenant) (last ($days) days)..."

    let audit_logs = (
        provisioning audit query \
            --tenant $tenant \
            --days $days
    )

    # Group by event type
    let by_type = ($audit_logs | group-by event_type)

    $by_type | each { | group |
        print $"($group.0.event_type): ($group | length) events"
    }

    # Highlight suspicious activity
    let suspicious = (
        $audit_logs
        | where severity == "high" or failed == true
    )

    if ($suspicious | length) > 0 {
        print ""
        print "⚠️  Suspicious Activity Detected:"
        $suspicious | each { | event |
            print $"  - ($event.timestamp): ($event.action) - ($event.description)"
        }
    }
}
```

## Network Isolation

```nickel
{
  network_isolation = {
    vpc_per_tenant = true,

    security_groups = [
      {
        name = "tenant-boundary",
        rules = [
          {
            direction = "ingress",
            protocol = "tcp",
            port_range = "0-65535",
            source = "same-vpc-only",
            description = "Allow traffic within tenant VPC only"
          },
          {
            direction = "egress",
            protocol = "tcp",
            port_range = "443",
            destination = "managed-services-only",
            description = "Limit egress to Provisioning-managed services"
          }
        ]
      }
    ],

    network_acls = {
      default_deny = true,
      allow_only_documented_flows = true
    }
  }
}
```

## Testing Multi-Tenancy

```rust
#[cfg(test)]
mod multi_tenancy_tests {
    use super::*;

    #[tokio::test]
    async fn test_tenant_data_isolation() {
        let tenant1_data = "secret-data-1".as_bytes();
        let tenant2_data = "secret-data-2".as_bytes();

        let cmk1 = kms.create_key("tenant-1").await.unwrap();
        let cmk2 = kms.create_key("tenant-2").await.unwrap();

        let encrypted1 = crypto::encrypt(tenant1_data, &cmk1).await.unwrap();
        let encrypted2 = crypto::encrypt(tenant2_data, &cmk2).await.unwrap();

        // Tenant 1 cannot decrypt Tenant 2's data
        let result = crypto::decrypt(&encrypted2, &cmk1).await;
        assert!(result.is_err());

        // Each tenant decrypts only their own data
        let decrypted1 = crypto::decrypt(&encrypted1, &cmk1).await.unwrap();
        assert_eq!(decrypted1, tenant1_data);
    }

    #[tokio::test]
    async fn test_resource_limits() {
        let tenant_id = "test-tenant";

        // Set limit to 5 instances
        limits.set(tenant_id, "compute", 5).await.unwrap();

        // Create 5 instances
        for i in 0..5 {
            instances.create(tenant_id, &format!("instance-{}", i)).await.unwrap();
        }

        // 6th instance should fail
        let result = instances.create(tenant_id, "instance-5").await;
        assert!(result.is_err());
    }
}
```

## SLA and Service Levels

```nickel
{
  tenant_slas = [
    {
      tier = "starter",
      monthly_cost = "$100-500",
      sla = "99.5%",
      support = "community",
      features = ["basic-workflows"]
    },
    {
      tier = "professional",
      monthly_cost = "$500-5000",
      sla = "99.9%",
      support = "email",
      features = ["batch-workflows", "multi-cloud", "custom-providers"]
    },
    {
      tier = "enterprise",
      monthly_cost = ">$5000",
      sla = "99.99%",
      support = "24/7-phone",
      features = ["all-features", "sso", "dedicated-support"]
    }
  ]
}
```

## See Also

- [Workspace Management](./workspace-management.md)
- [Security System](../features/security-system.md)
- [Authorization](../security/authorization.md)
