# Write tenant policy

Add a rule that limits what agents on a tenant may do, see what it would have done, and publish it as the next version.

A tenant rule is a limit on top of every agent's manifest. The manifest is the ceiling each agent
declared for itself (models, hosts, tools); a rule is the tenant saying "and also not this", for
every agent, for named agents, or for agents in a lifecycle status. Rules live in the Console
under **Policies**.

- A rule is a sentence: **who** it applies to, **what** it watches (model calls, tool calls, or
  prompt content, external hosts, or managed shell commands), and whether a match blocks or requires human approval. Hard blocks take precedence.
- The policy is published as **versions**. Editing never touches what is live: edits go into one
  pending change, kept as you type, and **Review and publish** makes them the next version, all
  together or not at all. **Versions** rolls back and restores.
- Whether a refusal is applied or only recorded is the tenant's posture, the **Observing /
  Enforcing** switch at the top of Rules, with the week's number beside it.
- Workspace administrators can publish. Workspace members can read, prepare a change, try it,
  and request a publish.

## Add a rule

1. Open **Policies** and choose **New rule**: start from a template (block specific models, block a
   tool for some agents, keep secrets out of prompts), write it as code, or upload a policy file.
2. The rule opens on its own screen. Name it: the name is what Activity and the ledger show when it
   fires.
3. **Applies to**: all agents, named agents, or agents in a lifecycle status such as
   `experimental`. Named agents and a status combine.
4. **Watch**: model calls or tool calls (name the ids, or anything outside the agent's manifest),
   or prompt content (terms, patterns, a length cap, the secrets detector).
5. The **Form** and **Code** tabs are two views of the same rule. Anything the form cannot say
   stays as code; the list still shows the sentence it can read.
6. **What it would do**, beside the form, replays the last seven days against the rule as you
   edit, and **Try a request** judges one request for a named agent (prompt-content rules run the
   pasted text through the real inspector).
7. Edits are kept as you type. **Back to rules** never loses work.

### When a prompt-content rule matches

A message someone sends is checked before its run starts. A match refuses the send and names the
rule; nothing reaches the agent, and the session stays usable for the next message. Text the agent
reads on its own, such as tool results, is checked when it goes to the model, and a match ends
that turn with a short refusal in place of the model's reply. Only new text is checked, so a
published rule applies from then on and does not re-judge a session's earlier messages.

## Review and publish

The bar at the bottom of Rules collects your change. **Review and publish** opens a sheet with
every changed rule as a before-and-after sentence, the whole new policy replayed over last week
(and a note when it blocks fewer requests than today's), and a summary line. **Publish** makes it
the next version: every engine source the change touches is validated, approved and activated
together, and if one is refused the ones already switched are put back before you see the error.

If you cannot publish, the same button becomes **Request publish**: the change stays saved with
your name on it and shows at the top of Rules for anyone who can.

## Versions

The live version is named at the top of Rules. **Versions** lists every publish with its author,
summary and what changed, and offers **Roll back** on the live one and **Restore** on older ones.
A restore is a publish of that older policy as a new version, through the same path, so nothing
is edited in place and every step is on the ledger.

## Observing and enforcing

The switch at the top of Rules says whether model-call refusals are applied or only recorded, and
the number beside it says how many the week would have refused. Turning enforcing on is a
platform-operator action; the confirmation names the number and says which planes follow: model
calls enforce, prompt-content and tool-call rules keep observing until the gateway is armed for
them.

## Read what happened

**Activity** lists decisions as what happened: Blocked, Would have blocked, Asked a human,
Allowed, or Could not decide, each naming the rule. **Why?** explains a refusal from the rule, the
reason and the manifest. **Make a rule** opens the rule screen pre-filled from the decision.

## External hosts and managed shell commands

Choose **Control external hosts** or **Control shell commands** from **New rule**. Select
**Block** or **Ask a human**. For approval rules, choose either:

- **Once:** approve this connection or exact command attempt.
- **During this run:** approve repeated connections to the same host and port, or the same
  exact command and working directory, while this run remains active. Other command inputs
  remain bound too. This never grants unrestricted access for the run.

Host rules accept exact hostnames, `*.example.com` for subdomains (not the base domain),
and an optional port. Command rules name executables such as `rm` or `curl`. Dynamic shell
expressions are treated conservatively as potentially matching command rules.

The operation waits in the **Supervision** inbox until a human approves or refuses it. Unanswered asks
expire after 30 minutes. A network client may time out earlier while waiting; approval
does not restart a request that the client has already abandoned. Approval cannot override a hard block or the agent's baseline
capability limits. Changing or restoring the action policy requires a fresh approval.
**Observing** records the decision without stopping to ask a human. Activity distinguishes
**Asked a human** from **Approved**; approval is not proof that execution succeeded.

**Coverage:** host rules check new external connections at the host proxy. An HTTPS connection
can carry multiple requests; this is not inspection of individual URLs or requests. Command
rules check managed shell tool input before execution. They do not intercept arbitrary
subprocesses or prevent file deletion through Python, scripts, or other programs. Filesystem
policy enforcement is not part of this feature.

Updated host workers and Claude or Deepagents runtimes are required before starting new
runs, including while observing, so switching to enforcement cannot bypass checks.
Enabling external host or command enforcement applies to runs that start after the
switch. Runs that started earlier, including suspended runs that resume later, keep
observing until they end: their matches are recorded as would-deny or would-ask and the
Assurance Ledger entry says the run started before enforcement. The **Policy enforcement**
dialog lists those runs with a **Stop** control for each. An unsupported runtime is refused
rather than silently bypassing an enforcing rule.

These rules use the `cedar / actionfacts/v1` family. Cedar evaluates the conditions;
validated annotations declare the approval requirement and its scope:

```cedar
@id("approve-partner-host")
@effect("require-approval")
@approval_scope("run")
forbid(principal, action == Action::"network.connect", resource)
when { context.target == "api.partner.example" && context.port == 443 };
```

The platform manages the human decision and checks its run, operation, and active policy
binding. A matching unannotated `forbid` remains a hard block.
