Writing Policies
Policies are the core governance rules in MITRITY. Every action an agent performs is evaluated against your policies before it executes. This guide covers policy structure, pattern matching, the priority system, and practical examples.
Policy Structure
Every rule has these components:
| Field | Type | Description |
|---|---|---|
name | string | A human-readable name for the rule (e.g., allow-crm-reads) |
tool_pattern | string | A glob or regex pattern matching action types |
action | enum | allow or deny |
alert | boolean | Emit an alert event when the rule matches |
hold | boolean | Suspend the action pending human approval |
priority | integer | Evaluation order — higher values are evaluated first |
constraints | object | Optional conditions on the action's arguments — see Constraints |
Policy types
- Allow: The action is permitted. The gateway forwards the request and logs the event.
- Deny: The action is blocked. The gateway returns an error to the agent and logs the event. Notifications are sent if configured.
- Alert: The action is permitted, but an alert is generated. The event is flagged in the audit log and notifications are sent to configured channels (Slack, email, SIEM).
- Hold: The action is paused and queued for human approval. The gateway holds the request until an authorized user approves or denies it in the dashboard, or until the hold timeout expires.
These four outcomes are not a single enum field. action is allow or deny, and alert and hold are booleans layered on top of it: an alert is action: allow with alert: true, and a hold is action: deny with hold: true — denied unless a human releases it.
Prompt injection modes
Beside its rules, a policy carries settings of its own, and two of them govern the prompt-injection scan. injection_mode (off · log · enforce) covers tool calls. llm_injection_mode (enforce · alert · off, default alert) covers the prompts an agent sends through the LLM Gateway: under enforce a prompt that carries an injection never reaches the model, under alert the call proceeds and the event is recorded, and off skips the scan for prompts only. The two are separate because an injection in a prompt does no harm by itself — the harm is the tool call it induces, and that call keeps the tool-call mode — while agents that read logs, web pages or tickets legitimately see injection-shaped text. injection_mode: off turns the scan off everywhere, prompts included.
Action Pattern Matching
Action patterns support both glob and regex syntax for matching action types.
What an action type looks like
Every action the edge evaluates carries a surface prefix, and nothing is prepended for you: a glob must cover the whole action type, and a regex must account for the prefix itself (anchor with ^ if you mean the start):
| Surface | Action type | Example |
|---|---|---|
| MCP tool call through the sidecar | mcp:<tool> | mcp:read_crm_contact |
| MCP tool call through the gateway | mcp:<namespace>__<tool> | mcp:fs__write_file, mcp:shell__run_command |
| MCP call across the mesh (Mesh Authorizer) | mcp:<service>__<operation> | mcp:content-tool__read_file, mcp:content-tool__GET / |
| LLM call through the LLM gateway | llm:send:<provider>:<model> | llm:send:openai:gpt-4o |
| Framework built-in tool decided through the hook | builtin:<tool> | builtin:bash |
| Governed shell (v0.21) | shell:execute | shell:execute |
A gateway tool's name is its upstream's namespace and the tool's own name joined by a double underscore (fs__write_file), because that is the only form the model APIs can call — Claude Code and the Claude Agent SDK register MCP tools as mcp__<server>__<tool>, and the Anthropic API accepts tool names matching ^[a-zA-Z0-9_-]{1,128}$ (OpenAI's cap is 64, so keep namespaces short). The gateway serves no other form, and the Mesh Authorizer names an east-west call the same way — the in-cluster service and the operation joined by __ — so one rule shape covers a tool reached either way. The surface prefix (mcp:, llm:send:, builtin:, shell:execute) is MITRITY's own grammar and is never sent to a model.
A pattern written as read_crm_* therefore never matches an MCP action; write mcp:read_crm_*.
The API enforces this when a rule is saved: a glob that does not start with a known surface — one of mcp, llm, http, egress, delegate, builtin or shell before its first : — or that puts a wildcard in the surface position (*:read_file would match every surface), or that keeps a colon after the mcp surface (mcp:github:* — the former namespace separator, which no action type carries any more; write mcp:github__*) is rejected with HTTP 422 naming tool_pattern, and the dashboard flags it before you submit. * alone (every action) and regex: patterns are accepted as written; the colons of the other surfaces (llm:send:openai:*) are their own grammar and are fine.
Glob patterns
Glob patterns use * as a wildcard for simple matching:
| Pattern | Matches | Does not match |
|---|---|---|
mcp:read_* | mcp:read_crm_contact, mcp:read_database | mcp:write_crm_contact |
mcp:*_export_* | mcp:bulk_export_contacts, mcp:data_export_csv | mcp:read_contacts |
mcp:db.* | mcp:db.query, mcp:db.insert, mcp:db.delete | mcp:api.query |
mcp:* | every MCP tool call | llm:send:openai:gpt-4o |
Globs follow Go's filepath.Match rules: * matches any run of characters (including : and ., but never / — which matters for path constraints such as paths), ? matches one character, [...] matches a character class, and the glob must cover the entire action type.
Regex patterns
For alternations and anchors, prefix the pattern with regex:. The remainder is a Go RE2 regular expression:
| Pattern | Matches |
|---|---|
regex:^mcp:(read|list)_crm_.*$ | mcp:read_crm_contact, mcp:list_crm_deals |
regex:^mcp:db\.(query|read)$ | mcp:db.query, mcp:db.read |
regex:^mcp:.*_(delete|drop|truncate)_.*$ | Any destructive operation |
How regex patterns behave:
- Matching is unanchored:
regex:crmmatchesmcp:read_crm_contact. Anchor with^and$when you mean the whole action type, as every example here does. - RE2 syntax: no backreferences or lookaround, and no pathological backtracking. Patterns are limited to 1024 bytes.
- A pattern that does not compile is rejected when the rule is saved (HTTP 422 with the field named). If one ever reaches the edge anyway, it matches nothing — a broken deny never turns into an allow.
- The same syntax works in list-valued constraints such as
paths,resolved_commandsandscript_paths, where a leading!negates the entry:"paths": ["regex:^/workspace/.*$", "!regex:^/workspace/secrets/"]. It does not apply to*_containsconstraints, which are plain substrings.
Priority System
Policies are evaluated in descending priority order — the highest priority number is evaluated first. The first matching policy determines the outcome.
Priority 300: deny mcp:bulk_export_* (evaluated first)
Priority 200: alert mcp:write_* (evaluated second)
Priority 100: allow mcp:read_crm_* (evaluated third)
Priority 50: allow mcp:send_email (evaluated fourth)
Priority 1: deny * (catch-all, evaluated last)
Best practice: Use a catch-all deny policy with the lowest priority (priority: 1) as a safety net. Then add explicit allow and alert policies with higher priorities for actions you want to permit or monitor.
Constraints
Constraints narrow a rule by the arguments of the action, not by who made it or when. A rule matches only if its action pattern matches and every one of its constraints is satisfied. A constraint that is not satisfied makes the rule not match at all — evaluation moves on to the next rule, it does not deny.
Constraints are the part of the policy model with the sharpest edges. This section documents what the edge actually implements today, including where it falls short.
The vocabulary
| Key | Value | Matches against |
|---|---|---|
paths | list of globs | the action's path argument |
commands | list of globs | Deprecated alias of resolved_commands from edge v0.20 — see Semantic command constraints. Before v0.20: the first word of the command argument (see the limitation below) |
<param>_contains | list of substrings | the named argument, case-insensitively — e.g. commands_contains, query_contains |
domains | list of globs | the action's domain argument |
channels | list of globs | the action's channel argument |
models | list of globs | the action's model argument |
max_tokens | number | the action's max_tokens argument, as an upper bound |
pattern | string | free-form, for custom tools |
These are the keys the dashboard's rule editor writes. The edge does not keep an allowlist of keys, so any key of the right shape works the same way: <key> resolves to the argument named <key> with a trailing s stripped (paths → path), then to <key> itself.
Egress destinations are a separate mechanism. The
domainsconstraint compares a tool argument. To control which hosts an agent may reach on the network, use destination allowlists, which are enforced against the tool's attested destination rather than an argument value.
Value types, and one trap
| JSON value | Effect |
|---|---|
| Array of strings | Pattern list |
| String | Pattern list with one entry |
| Number | Numeric upper bound |
| Object | Silently ignored |
The last row is the trap. The control plane stores constraints verbatim without validating them, and the edge drops any key whose value is an object. A constraint written as {"time": {"after": "18:00"}} is accepted by the API, never evaluated, and the rule behaves as if the constraint were absent.
There is no time-of-day, rate-limit, per-agent or hold-timeout constraint today. Use the mechanisms that do exist instead:
| Instead of | Use |
|---|---|
| A time-window constraint | Nothing equivalent today — scope by action pattern, or use an alert rule and review the audit log |
| A rate constraint | LLM Gateway rate limits for LLM calls — a per-policy cap on calls per minute per agent |
An agent_id constraint | Assign a different policy to that agent. An agent_id key is compared against a tool argument, not the calling agent, so it never matches what you meant |
A hold_timeout constraint | hold_timeout_minutes on the rule itself — see approval workflows |
Glob matching and ! exclusion
Pattern lists use glob syntax — *, ?, [...] — with three refinements:
- Paths are canonicalised first.
/workspace/../../etc/shadowresolves to/etc/shadow, so it does not match a/workspacepattern. Traversal cannot slip past a prefix. - A pattern ending in
/*matches recursively./tmp/*matches/tmp/a/b/c, not just/tmp/a. - A pattern with no glob characters matches as a directory prefix.
/workspacematches/workspaceand anything beneath it.
A ! prefix makes an entry an exclusion, and the three rules are applied in order:
- If the value matches any excluded pattern → the constraint fails.
- Otherwise, if there is at least one positive pattern, the value must match one of them.
- If only exclusions were given and none matched → the constraint passes.
So ["/workspace/*", "!/workspace/.git/*"] means "anywhere under /workspace except .git", and ["!/etc/*"] alone means "anywhere except /etc".
_contains matching
A key ending in _contains switches to case-insensitive substring matching, against the argument named by the prefix: query_contains reads the query argument. Exclusion and the three rules above work identically.
{
"name": "alert-on-destructive-sql",
"action": "allow",
"alert": true,
"tool_pattern": "mcp:query_database",
"priority": 200,
"constraints": {
"query_contains": ["drop table", "truncate", "delete from"]
}
}
Numeric limits
A numeric constraint is an upper bound. The constraint fails — and so the rule does not match — when the argument parses as an integer strictly greater than the limit. An argument that is missing, non-numeric, or zero never trips it.
What is compared when the argument is missing
If neither <key> nor its singular form is present in the action's arguments, the edge falls back to the action's target, which is the qualified tool name.
This matters more than it looks. A paths constraint on a tool whose argument is called file rather than path will compare your globs against the string fs__write_file. It will not match, so a deny rule will not fire and an allow rule will not allow. Check the tool's actual argument names in the tool catalog before writing a constraint against them.
The commands limitation
This section describes edge releases before the semantic command parser below. From the next release
commandsis an alias ofresolved_commandsand the evasion described here no longer works — see Semantic command constraints.
commands does not see the whole command line. The value is normalised to the base name of the first word — /bin/rm -rf / and ./rm -rf / both become rm — and truncated at the first null byte.
That normalisation is what makes commands easy to evade:
{
"name": "deny-rm",
"action": "deny",
"tool_pattern": "mcp:shell__run_command",
"priority": 300,
"constraints": { "commands": ["rm", "dd", "mkfs"] }
}
This rule does not match bash -c "rm -rf /", because the first word normalises to bash. It also does not match rmdir, which is correct, and it does not match a tool that names its argument cmd, args or script, which is not.
Until commands is replaced, the workaround is two rules rather than one — deny the wrappers by name, and deny the dangerous strings wherever they appear:
[
{
"name": "deny-shell-wrappers",
"action": "deny",
"tool_pattern": "mcp:shell__run_command",
"priority": 310,
"constraints": { "commands": ["bash", "sh", "zsh", "env", "sudo", "nohup", "xargs"] }
},
{
"name": "deny-destructive-strings",
"action": "deny",
"tool_pattern": "mcp:shell__run_command",
"priority": 305,
"constraints": { "commands_contains": ["rm -rf", "mkfs", "dd if=", "| sh"] }
}
]
Treat this as mitigation, not enforcement. Denying the wrappers by name also blocks legitimate shell use, and substring matching is defeated by quoting, variables, and encoding — rm -rf with two spaces already slips past the list above. commands is deprecated and is not extended; the keys below replace it.
Semantic command constraints
These are available from edge release v0.21. What follows describes shipped behavior. The parser reads the command line with a real shell grammar, unwraps wrappers, pipelines and inline interpreters, and exposes the result as the constraint keys below. The wrapper evasion in the previous section becomes a block.
commands is now an alias of resolved_commands, with the same semantics: the same globs, the same any/all matching, the same unwrapping. A rule that still carries commands therefore starts matching the whole parsed tree instead of basename(argv[0]), and bash -c "rm -rf /" stops slipping past it. The key stays deprecated — write resolved_commands in new rules, because the name says what it matches — but nothing you have already written breaks.
The parser reads whichever command-shaped argument the tool exposes: command, cmd or script as a command line, args or argv as a JSON array of words. A code argument is content, not a command: a code-authoring tool's body is treated like a written script (hashed, DLP- and injection-scanned, and parsed as shell when it is one — see the write-time findings below), and only a tool whose name says it executes that code has it judged as a command line. On every other tool a code body no rule judged — a path-less call, or a written body that is not a shell program — carries code_body_unjudged, with the tool and the key in its metadata; a shell body written through such a tool is judged as the script being authored (script_authoring) and needs no stamp. Two policy settings govern this — see Code bodies on tools that do not execute them.
| Key | Value | Semantics |
|---|---|---|
resolved_commands | list of globs | Every command the parser resolves after unwrapping. Deny rules match if any resolved command matches; allow rules match only if every resolved command matches. So bash -c "rm -rf /" resolves to bash and rm, and a deny on rm fires |
commands | list of globs | Deprecated alias of resolved_commands, identical in behavior |
resolved_commands_contains | list of substrings | The substring form of the above, with the same any/all semantics |
exec_features | list of feature names | Named properties of the parsed command line: pipe_to_shell, remote_fetch_to_shell, base64_decode, eval, cmd_subst, subshell, inline_interpreter, redirect_write, heredoc, background, find_exec, xargs, unparseable, depth_exceeded, nul_byte, unsupported_dialect, unresolved_command, paths_truncated |
script_paths | list of globs | Script files the command line references, matched with the same glob and ! semantics as paths |
max_pipeline_depth | number | Upper bound on pipeline and nesting depth |
on_unparseable | deny | hold | allow | What to do when the command line cannot be parsed, or when the command word itself cannot be resolved. Defaults to deny |
Two of the features describe the analysis rather than the command, and they are worth reading before you write a rule against either:
unresolved_command— the command word is dynamic: a variable, a substitution or an unreadable token, so no base command could be resolved.$CMD -rf /and$(which rm) -rf /contribute nothing toresolved_commands, which means a denylist never sees them. The edge fails closed on this throughon_unparseable, and naming the feature lets you say so explicitly — or alert on it while still allowing. A variable in argument position (echo $HOME) raises nothing.paths_truncated— more than 64 paths were referenced by a single command, so that command's path list was truncated. The coverage is not: the parsed tree, the resolved commands and the script references are all complete, and this is lost audit detail rather than a hole. It is deliberately notdepth_exceeded, which does mean the analysis stopped early — treating them alike would deny an ordinarygit addover a long file list.
Constraint validation ships with the same work. The control plane rejects object and boolean constraint values, and any exec_features entry outside the list above, with a 422 naming the offending key — instead of accepting them and silently dropping them at the edge. Unknown keys are still accepted, because the constraint vocabulary is open by design (see "The vocabulary" above).
For what is and is not enforced across the rest of the product, see Governance Coverage.
Scripts, write-then-run and autorun paths
Parsing the command line answers "what does this command run?". It does not answer "what happens when the agent puts the commands in a file first?" — the split write-and-run, and the smuggled script. Those are closed by three mechanisms on the edge, each governed by a policy-level setting rather than a rule constraint. The settings live on the policy itself (Policies → edit → Command Analysis), beside command_analysis_mode:
| Setting | Values | Default | Governs |
|---|---|---|---|
command_analysis_mode | off · enforce · log | enforce | Whether the parser, the taint store, script inspection and the write-time findings run at all. Under log every finding is recorded and none of the actions below is consulted. |
write_then_run_window_minutes | 1–1440 | 30 | How long a governed write stays linked to a later execution of the same path. |
taint_action | alert · hold · deny | alert | What happens to an execution that reads a file this agent wrote earlier (write_then_run), or whose script changed after that write (modified_outside_governance). |
autorun_write_action | alert · hold · deny | alert | What happens to a write whose destination runs by itself (write_to_autorun_path). |
script_inspection_roots | up to 32 absolute directories | empty = the working directory | Where the edge may read script files from before deciding. |
unjudged_code_action | alert · hold · deny | alert | What happens to an action carrying code_body_unjudged — a code body handed to a tool the edge does not treat as executing. |
code_execution_tools | up to 64 tool patterns, each at most 1024 bytes | empty = execution-named tools only | Which tools execute their code argument: on a matching tool the body is parsed and judged as the command, and code_body_unjudged is never raised. Same grammar as a rule's tool_pattern. |
alert records the finding on the event and lets the rules decide; hold parks the action for approval like any other hold; deny blocks it with blocked_by: command. A rule that denies the resolved commands wins over a policy-level alert — the settings are the floor for findings no rule mentions, not a cap on what a rule may do.
The taint store
Every allowed write-shaped action — builtin:write, builtin:edit, an MCP tool with a path and content, a shell tree with a redirect, tee, cp, mv, curl -o, chmod — leaves an entry in a per-agent, per-session store: the path, the writer tool, the time, and the SHA-256 of the bytes when the edge saw them in full. Entries expire after write_then_run_window_minutes. When a later execution references one of those paths, the event carries write_then_run and a taint hit (path, age, writer, hash status); if the file's current hash no longer matches the governed write, modified_outside_governance too. taint_action decides the outcome.
The hash status has three values: matches, changed, and unknown — an Edit-style patch, a redirect or a cp produces a file the edge never saw in full, so no hash is recorded and nothing is claimed either way. An MCP write that carries more than one distinct content body (content beside code) is unknown too: every body is judged, but which one lands is not the edge's to know, so the write carries ambiguous_write_content naming the keys and modified_outside_governance cannot fire for that path.
Script inspection
Every script reference in the parsed tree — sh f, bash f, . f, source f, python f, a bare ./f — is resolved against the action's working directory and read under script_inspection_roots (absolute, cleaned, symlinks resolved, regular files only, 256 KiB cap). Shell scripts are parsed with the same grammar and merged into the evaluated tree, so resolved_commands, exec_features, script_paths and the threat pack all see the commands inside them: a deny rm rule fires on bash deploy.sh when deploy.sh contains rm -rf /. Python, Node and other interpreter scripts are hashed and scanned, not analyzed.
A script the edge cannot read — outside every root, missing, oversize, or reached through a symlink that leaves the roots — is script_unresolvable: its path is listed, no hash is recorded, and the rule's on_unparseable applies (deny by default). execution.script_unresolvable notifies so you can widen the roots when the denial is a configuration gap rather than an attack. Widening the roots lets the edge read more and therefore judge more; it never makes a script trusted.
Write-time findings
Two findings are raised on the write, where a hold is cheapest. script_authoring: the written content is a program (a shebang, a shell extension, .git/hooks/, or content that parses as shell with a command and an exec feature) — it is parsed and evaluated by every rule whose tool_pattern matches the write, so a deny on builtin:write with resolved_commands: ["rm"] stops the agent from authoring rm -rf / into a script. write_to_autorun_path: the destination runs without anyone launching it — cron, systemd units, launchd plists, shell rc and profile files, .git/hooks/**, package.json lifecycle scripts, .claude/settings*.json, .mcp.json — and autorun_write_action decides.
Code bodies on tools that do not execute them
A code argument is content (see Semantic command constraints): only a tool the edge treats as executing has it parsed as the command line. Out of the box that is the execution-name gate — a tool whose name carries a token such as execute, run, eval or repl. code_execution_tools extends it with patterns of your own: mcp:pyodide, mcp:*repl*, regex:^mcp:.*(exec|run), written in the same grammar as a rule's tool_pattern and validated the same way (a pattern without a surface prefix is refused with a 422 naming the entry, code_execution_tools[<i>]). A code body handed to a matching tool is judged as the command the tool runs — the resolved_commands rules, the threat pack and on_unparseable all apply — and never carries the stamp.
On every other tool a non-blank code body no rule judged carries code_body_unjudged, and unjudged_code_action decides: alert (the default) records the coverage finding and leaves the outcome to the rules; hold parks the action for approval; deny blocks it. A recurring code_body_unjudged on one tool is a signal to name that tool in code_execution_tools rather than to tighten the action: the body is then judged instead of stamped. Both settings ride the same policy screen as the settings above, under Code bodies.
Example A: deny the split write-and-run
Policy settings:
{
"command_analysis_mode": "enforce",
"write_then_run_window_minutes": 60,
"taint_action": "deny",
"script_inspection_roots": ["/workspace"]
}
Rule:
{
"name": "deny-destructive-anywhere",
"action": "deny",
"tool_pattern": "regex:^(builtin:(bash|write|edit)|mcp:shell:.*)$",
"priority": 300,
"constraints": { "resolved_commands": ["rm", "dd", "mkfs*"] }
}
The agent writes /workspace/cleanup.sh containing rm -rf /data through builtin:write. The content is a program, so the write carries script_authoring, its tree is evaluated by the rule (the pattern matches builtin:write), rm resolves, and the write is denied — nothing reaches disk. Had the rule not matched the write action, the write would have been allowed and tainted; the later bash /workspace/cleanup.sh would then be read under /workspace, rm would resolve from the merged tree and the rule would deny it — and even without the rule, write_then_run with taint_action: deny blocks it. Either way the audit event shows the canonical command, script_authoring or write_then_run, the script's hash and the taint hit.
Example B: hold writes to autorun paths, watch the rest
Policy settings:
{
"command_analysis_mode": "enforce",
"taint_action": "alert",
"autorun_write_action": "hold",
"script_inspection_roots": ["/workspace", "/opt/ci/bin"]
}
No rule is needed. A builtin:write to /workspace/.git/hooks/pre-commit is a write to an autorun path: the event carries write_to_autorun_path (and script_authoring, since a hook is a program), and the action is held for approval — the reviewer sees the canonical script text and decides. A bash /opt/ci/bin/release.sh that reads a file the agent wrote ten minutes earlier is allowed, carries write_then_run and its taint hit, and produces one execution.write_then_run_observed notification per path within the window; a bash /srv/other.sh outside both roots is script_unresolvable and denied by the default on_unparseable, with an execution.script_unresolvable notice naming the path.
Until the first-party governed shell runs the execution, the hash is recorded at decision time and a file can still change before the framework runs it — modified_outside_governance catches the change on the next decision, it does not prevent it. See Governance Coverage for the boundary.
Practical Examples
Example 1: Basic CRM governance
[
{
"name": "deny-bulk-export",
"action": "deny",
"tool_pattern": "mcp:bulk_export_*",
"priority": 300
},
{
"name": "alert-write-operations",
"action": "allow",
"alert": true,
"tool_pattern": "mcp:write_crm_*",
"priority": 200
},
{
"name": "allow-crm-reads",
"action": "allow",
"tool_pattern": "mcp:read_crm_*",
"priority": 100
},
{
"name": "deny-all-default",
"action": "deny",
"tool_pattern": "*",
"priority": 1
}
]
Example 2: Hold for privilege escalation
{
"name": "hold-iam-changes",
"action": "deny",
"hold": true,
"tool_pattern": "regex:^mcp:iam\\.(create_role|assign_role|escalate_permissions)$",
"priority": 400
}
The hold timeout is not a constraint — hold_timeout_minutes and timeout_action are fields on the rule itself. See approval workflows.
Example 3: Writes confined to a workspace
[
{
"name": "deny-writes-to-git-internals",
"action": "deny",
"tool_pattern": "mcp:fs__write_file",
"priority": 310,
"constraints": {
"paths": ["/workspace/.git/*"]
}
},
{
"name": "deny-writes-outside-workspace",
"action": "deny",
"tool_pattern": "mcp:fs__write_file",
"priority": 300,
"constraints": {
"paths": ["!/workspace/*"]
}
}
]
The second rule denies any write whose path argument is not under /workspace: the list is exclusion-only, so everything outside the workspace satisfies it. The first rule runs ahead of it on priority and carves .git back out. Both depend on the tool naming its argument path — check the tool catalog first.
Managing Policies
Policies can be created and managed through:
- Dashboard: Policies > Create Policy — visual editor with validation
- REST API:
POST /api/v1/policies— programmatic management - Policy-as-code: Define policies in JSON files and apply through CI/CD
For the full API reference, see the API overview.