# Team Quotas

Team Quota protects shared regional capacity and bandwidth from one team. It is
an operational admission guardrail, not pricing or billing. A team-specific
PostgreSQL policy overrides the regional default for the same dimension.

## Dimensions

| Dimension | Kind | Unit | Enforcement |
| --- | --- | --- | --- |
| `active_sandboxes` | capacity | count | Sandbox claim transaction |
| `sandbox_claims` | rate | claims | External claim request |
| `api_requests` | rate | requests | External regional/full-mode gateway request |
| `network_egress_bytes` | rate | bytes | ctld network runtime |
| `network_ingress_bytes` | rate | bytes | ctld network runtime |

Capacity policies compare current transactional usage with `limit_value`. Rate
policies refill `limit_value` tokens every `interval_ms` and allow an immediate
burst up to `burst_value`.

Network byte quotas are bandwidth controls, not lifetime transfer caps. TCP and
other streams wait for reserved tokens. UDP is dropped when tokens are not
immediately available so a datagram cannot block a ctld worker indefinitely.

CPU and memory are exact sandbox resource leases and metered usage, but are not
Team Quota dimensions. Node-capacity admission still rejects a claim when the
dedicated pool lacks CPU, memory, carrier, or NBD capacity.

## Regional Defaults

Configure manager directly with `default_team_quotas`:

```yaml
default_team_quotas:
    - dimension: active_sandboxes
      limit_value: 100
    - dimension: sandbox_claims
      limit_value: 5
      interval_ms: 1000
      burst_value: 5
    - dimension: network_egress_bytes
      limit_value: 12500000
      interval_ms: 1000
      burst_value: 25000000
    - dimension: network_ingress_bytes
      limit_value: 12500000
      interval_ms: 1000
      burst_value: 25000000
```

Manager declaratively reconciles these defaults into PostgreSQL. Team-specific
policies stored in PostgreSQL take precedence. Specify interval and burst
explicitly for rate quotas.

Region-wide rate admission requires one shared Redis authority reachable by
all relevant gateway and ctld processes. Use one region/key prefix and team
scope; do not include cluster ID in a team bandwidth key. Configure Redis to
fail closed for quota enforcement unless you have deliberately accepted a
fail-open policy.

`sandbox_claims` is charged only for `POST /api/v1/sandboxes`. Resume and fork
are separate lifecycle operations. `api_requests` is charged once at the
external gateway; an internally authenticated regional-to-cluster hop must not
charge it again.

## Other Limits

These controls are intentionally separate from Team Quota:

- exact node CPU/memory leases are physical capacity scheduling;
- sandbox service route limits are application policy;
- per-sandbox bandwidth is workload QoS;
- function concurrency belongs to one sandbox runtime; and
- database, telemetry, node-channel, and global-gateway limits protect platform
  components.

## Inspect Quotas

Use `GET /api/v1/quotas` for all five dimensions or
`GET /api/v1/quotas/{'{dimension}'}` for one.

Capacity status includes `current` and `remaining`. Rate status includes
`interval_ms` and `burst_value`; `current` and `remaining` are `null` because
Redis token state is transient admission state, not cumulative usage.

## Read Cumulative Usage

Use the official SDK usage resource when another product needs cumulative
usage. It calls `GET /api/v1/usage/windows`, derives the team from the
authenticated token, and requires `usage:read`.

```ts
const page = await client.usage.listWindows({
    cursor,
    limit: 1000,
    windowType: "sandbox.runtime_mib_milliseconds",
});
```

The response contains immutable closed windows and an opaque `nextCursor`.
Persist the cursor only after the page is durably consumed. The API is served
from the ClickHouse metering projection and returns unavailable when that query
backend is disabled or unhealthy.

Do not give consuming products ClickHouse credentials, PostgreSQL access, or
internal metering endpoints.

## Metering Boundary

Raw ingress/egress metering remains available for usage export and showback.
PostgreSQL metering producer state and its ClickHouse read model record
transferred bytes, but cumulative totals are not network-admission counters.

Metering records actual resource leases. It does not infer sandbox CPU or
memory from the Nomad carrier's overhead reservation or an immutable runtime
class.

## Legacy Network Fields

Move old `team_egress_bandwidth_bytes_per_second`,
`team_ingress_bandwidth_bytes_per_second`, and `team_bandwidth_burst_bytes`
values into `default_team_quotas` using `network_egress_bytes` and
`network_ingress_bytes`. The old fields are bootstrap compatibility only and
should not be used in new configuration.

Per-sandbox `egress_bandwidth_bytes_per_second`,
`ingress_bandwidth_bytes_per_second`, and `bandwidth_burst_bytes` remain
workload QoS controls.
