> ## Documentation Index
> Fetch the complete documentation index at: https://namespace.so/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Tenant Policies

> Limit what each tenant can use, such as how many instances it runs at once, and change those limits safely.

Policies control what a tenant can use.
With them, you can match each customer's limits to their plan, for example five concurrent instances for a starter plan and more for enterprise customers, or expire a trial tenant after 30 days.

This page covers reading a tenant's policies, replacing them, and changing one setting while keeping the rest.

## Credentials

Reading and writing policies use different credentials:

* **Writing** policies uses a [partner client](/docs/platform/tenants/clients#partner-client), because the request names the tenant. The examples call it `partnerClient`.
* **Reading** policies uses a [tenant client](/docs/platform/tenants/clients#tenant-client), because the request has no tenant ID and applies to the tenant the token belongs to. The examples call it `tenantClient`.

## How policies are stored

A tenant has a list of policies and a revision number.
Every successful change replaces the whole list and increments the revision.

The main policy is the usage policy, identified by `namespace.cloud.compute.v1beta.UsagePolicy`.
Its settings are stored as a JSON string in the policy's `value`:

```json theme={null}
{
  "policies": [
    {
      "policy": "namespace.cloud.compute.v1beta.UsagePolicy",
      "value": "{\"concurrency_limits\":{\"max_instance_count\":\"5\"}}"
    }
  ],
  "revision": "2"
}
```

A new tenant starts with an empty usage policy, whose value is `{}`, at revision 1.

## Set a tenant's policies

To give a tenant a fixed set of limits, for example when a customer picks a plan, send the complete policy list.
It replaces whatever the tenant had before, so there is no need to read the current policies first.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { create, toJsonString } from "@bufbuild/protobuf";
  import { UsagePolicySchema } from "@namespacelabs/sdk/proto/namespace/cloud/compute/v1beta/limits_pb";

  const usagePolicy = create(UsagePolicySchema, {
    concurrencyLimits: { maxInstanceCount: 5n },
  });

  await partnerClient.tenants.updatePolicies({
    tenantId: "tenant_lqrj7qre0ts32",
    policies: [
      {
        policy: "namespace.cloud.compute.v1beta.UsagePolicy",
        value: toJsonString(UsagePolicySchema, usagePolicy),
      },
    ],
  });
  ```

  ```go Go theme={null}
  import (
  	"google.golang.org/protobuf/encoding/protojson"
  	computepb "namespacelabs.dev/integrations/proto/namespace/cloud/compute/v1beta"
  )

  usagePolicy := &computepb.UsagePolicy{
  	ConcurrencyLimits: &computepb.UsagePolicy_ConcurrencyLimits{MaxInstanceCount: 5},
  }

  value, err := protojson.Marshal(usagePolicy)
  if err != nil {
  	log.Fatal(err)
  }

  _, err = partnerClient.Tenants.UpdatePolicies(ctx, &iamv1beta.UpdatePoliciesRequest{
  	TenantId: "tenant_lqrj7qre0ts32",
  	Policies: []*iamv1beta.TenantPolicy{{
  		Policy: "namespace.cloud.compute.v1beta.UsagePolicy",
  		Value:  string(value),
  	}},
  })
  ```
</CodeGroup>

Serializing the usage policy with its SDK type, rather than writing the JSON by hand, keeps field names and number formats correct.

## Read a tenant's policies

<CodeGroup>
  ```typescript TypeScript theme={null}
  const { policies, revision } = await tenantClient.tenants.describePolicies({});
  ```

  ```go Go theme={null}
  current, err := tenantClient.Tenants.DescribePolicies(ctx, &iamv1beta.DescribePoliciesRequest{})
  if err != nil {
  	log.Fatal(err)
  }
  ```
</CodeGroup>

The response has the same shape as the example in [How policies are stored](#how-policies-are-stored).

## Change one setting and keep the rest

Setting policies replaces the whole list, so changing one limit that way would drop every other setting.
To change a single setting, read the current policies, change that setting, and write them back with the revision you read.

<Steps titleSize="h3">
  <Step title="Read the current policies">
    <CodeGroup>
      ```typescript TypeScript theme={null}
      const current = await tenantClient.tenants.describePolicies({});
      ```

      ```go Go theme={null}
      current, err := tenantClient.Tenants.DescribePolicies(ctx, &iamv1beta.DescribePoliciesRequest{})
      if err != nil {
      	log.Fatal(err)
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Change the setting">
    Find the usage policy, parse its value, and change only the setting you need.
    This example raises the tenant's CPU limit and leaves every other setting and policy as it was.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      import { create, fromJsonString, toJsonString } from "@bufbuild/protobuf";
      import {
        UsagePolicy_ConcurrencyLimitsSchema,
        UsagePolicySchema,
      } from "@namespacelabs/sdk/proto/namespace/cloud/compute/v1beta/limits_pb";

      const policies = current.policies.map((policy) => {
        if (policy.policy !== "namespace.cloud.compute.v1beta.UsagePolicy") {
          return policy;
        }

        const usage = fromJsonString(UsagePolicySchema, policy.value);
        usage.concurrencyLimits ??= create(UsagePolicy_ConcurrencyLimitsSchema);
        usage.concurrencyLimits.maxCpu = 16n;

        return { ...policy, value: toJsonString(UsagePolicySchema, usage) };
      });
      ```

      ```go Go theme={null}
      for _, policy := range current.Policies {
      	if policy.Policy != "namespace.cloud.compute.v1beta.UsagePolicy" {
      		continue
      	}

      	usage := &computepb.UsagePolicy{}
      	if err := protojson.Unmarshal([]byte(policy.Value), usage); err != nil {
      		log.Fatal(err)
      	}
      	if usage.ConcurrencyLimits == nil {
      		usage.ConcurrencyLimits = &computepb.UsagePolicy_ConcurrencyLimits{}
      	}
      	usage.ConcurrencyLimits.MaxCpu = 16

      	value, err := protojson.Marshal(usage)
      	if err != nil {
      		log.Fatal(err)
      	}
      	policy.Value = string(value)
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Write the policies back with the revision">
    <CodeGroup>
      ```typescript TypeScript theme={null}
      await partnerClient.tenants.updatePolicies({
        tenantId: "tenant_lqrj7qre0ts32",
        policies,
        revision: current.revision,
      });
      ```

      ```go Go theme={null}
      _, err = partnerClient.Tenants.UpdatePolicies(ctx, &iamv1beta.UpdatePoliciesRequest{
      	TenantId: "tenant_lqrj7qre0ts32",
      	Policies: current.Policies,
      	Revision: current.Revision,
      })
      ```
    </CodeGroup>

    The update only succeeds if the tenant's revision still matches the one you read.
    If someone else changed the policies in the meantime, it fails with a `FailedPrecondition` error instead of overwriting their change. Read the policies again and repeat the change.

    Reading the policies again shows the new value and revision:

    ```json Output theme={null}
    {
      "policies": [
        {
          "policy": "namespace.cloud.compute.v1beta.UsagePolicy",
          "value": "{\"concurrency_limits\":{\"max_cpu\":\"16\", \"max_instance_count\":\"5\"}}"
        }
      ],
      "revision": "3"
    }
    ```
  </Step>
</Steps>

## Expire a tenant

An expiration policy sets a date when the tenant expires, which suits trial tenants.
Add it to the policy list next to the usage policy:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { timestampFromDate } from "@bufbuild/protobuf/wkt";

  const current = await tenantClient.tenants.describePolicies({});

  await partnerClient.tenants.updatePolicies({
    tenantId: "tenant_lqrj7qre0ts32",
    policies: [
      ...current.policies,
      {
        expirationPolicy: {
          expiresAt: timestampFromDate(new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)),
        },
      },
    ],
    revision: current.revision,
  });
  ```

  ```go Go theme={null}
  import "google.golang.org/protobuf/types/known/timestamppb"

  current, err := tenantClient.Tenants.DescribePolicies(ctx, &iamv1beta.DescribePoliciesRequest{})
  if err != nil {
  	log.Fatal(err)
  }

  _, err = partnerClient.Tenants.UpdatePolicies(ctx, &iamv1beta.UpdatePoliciesRequest{
  	TenantId: "tenant_lqrj7qre0ts32",
  	Policies: append(current.Policies, &iamv1beta.TenantPolicy{
  		ExpirationPolicy: &iamv1beta.TenantPolicy_ExpirationPolicy{
  			ExpiresAt: timestamppb.New(time.Now().Add(30 * 24 * time.Hour)),
  		},
  	}),
  	Revision: current.Revision,
  })
  ```
</CodeGroup>

To remove an expiration, send the expiration policy without `expiresAt`.

## Usage policy settings

| Setting | Description |
| - | - |
| `concurrencyLimits.maxCpu` | Total vCPUs the tenant can use at once. |
| `concurrencyLimits.maxMemoryMb` | Total memory, in MB, the tenant can use at once. |
| `concurrencyLimits.maxInstanceCount` | Number of instances the tenant can run at once. |
| `usageLimits.unitMinutes` | Unit minutes the tenant can use in a period. |
| `usageLimits.builds` | Builds the tenant can run in a period. |
| `usageLimits.wallSeconds` | Wall-clock seconds the tenant can use in a period. |
| `enabledPlatforms` | Platforms the tenant can use: `linux/amd64`, `linux/arm64`, `macos/arm64`, or `windows/amd64`. |
| `perPlatformLimits` | Limits for one platform, keyed by platform name. Each entry can set its own `concurrencyLimits`, the largest machine shape allowed with `shapeLimits.largestAcceptableShape`, and concurrency limits per shape with `perShapeLimits`. |

## Next steps

<Columns cols={2}>
  <Card title="Delete tenants" icon="trash-2" href="/docs/platform/tenants/delete">
    Remove a tenant when a customer leaves.
  </Card>

  <Card title="Resource limits" icon="gauge" href="/docs/platform/compute/resource-limits">
    How limits apply to the compute a tenant runs.
  </Card>
</Columns>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.