---
title: Environment freeze
description: Freeze an environment so agents can't change anything in it, mark
  it degraded, in maintenance or in an emergency, and lift the freeze.
url: https://hexelstudio.com/docs/titan/environment-freeze
---

An environment state applies to every agent that runs in one of your organization's environments (`env_...`, see [Workspaces](https://hexelstudio.com/docs/identity/workspaces)). The most useful is **frozen**: while an environment is frozen, Titan refuses every action that would change something there, and reads still run.

## States

| `state` | What Titan does |
| --- | --- |
| *(not listed)* | Active, the default. Nothing changes. |
| `frozen` | Titan refuses every action whose capability is not read-only, **before** policy is evaluated, with the reason `environment_frozen`. An action that was already approved is not run either. Read-only capabilities still run. |
| `degraded`, `maintenance`, `emergency` | Titan refuses nothing itself. The state is passed to your policy as `environment.state`, so your own rules can match it (for example, require approval for writes during `maintenance`). |

Each state needs a `reason_code`: `CHANGE_FREEZE`, `OPERATOR_REQUEST`, `SECURITY_INCIDENT`, `MAINTENANCE_WINDOW` or `POLICY_VIOLATION`.

A freeze holds until you lift it. If Titan can't reach the service that signs environment states, the last states stay in force, and a deployment that has published states but can't load them refuses changes (reason `environment_state_unavailable`) rather than letting them through.

## Freeze an environment

Environment states belong to a Titan deployment. Use the deployment's id (`td-...`, from **Titan → Deployments** in the console). You need `titan:policy:read` to read the states and `titan:policy:publish` to change them.

1. Read the current states and their version:

   ```bash
   curl -i https://api.hexelstudio.com/v1/titan/management/deployments/$TITAN_DEPLOYMENT_ID/environment-states \
     -H "Authorization: Bearer $ACCESS_TOKEN"
   ```

   The response lists `environments`, the `version` in force (also sent as the `ETag` header), and `enforced`.

2. Replace the list. Send every environment that should not be active; an environment you leave out becomes active again. Send the version from step 1 in `If-Match`, so that you don't overwrite a change someone else made since:

   ```bash
   curl -X PUT https://api.hexelstudio.com/v1/titan/management/deployments/$TITAN_DEPLOYMENT_ID/environment-states \
     -H "Authorization: Bearer $ACCESS_TOKEN" \
     -H "Content-Type: application/json" \
     -H 'If-Match: "<version>"' \
     -d '{
       "environments": [
         { "environment_id": "env_0123456789abcdef", "state": "frozen", "reason_code": "CHANGE_FREEZE" }
       ]
     }'
   ```

   If the states changed since you read them, the request fails with `412` and nothing changes. Read them again and retry.

The deployment's page in the console also shows its environment states.

## Lift a freeze

Send the list again without the environment, or with `{"environments": []}` to make every environment active.

## Check that it is enforced

The read returns `enforced`. On Titan Cloud it is `true`. When `enforced` is `false`, the states are stored but Titan does not apply them, and the console shows them as **not enforced**.

Every action Titan refuses because of a freeze is recorded in the [Agent Ledger](https://hexelstudio.com/docs/titan/agent-ledger) with the reason `environment_frozen`. To find them, filter with `reason_code=environment_frozen`.
