KGL (Kiro Governance Language) is the YAML format of a Kiro Enterprise Management policy. This page is for administrators who edit a policy in the YAML view of the console's Controls page, with the AWS CLI, or with the API. Kiro Enterprise Management is in preview for new Kiro profiles created in US East (N. Virginia) (us-east-1). It is on for all newly created profiles there; existing profiles are not affected by this change. The console's Capability catalog and Settings catalog, in the info pane of the Controls page, list the same capabilities and settings with their descriptions and shipped defaults. Which capabilities each client enforces is listed in Enforcement status. To publish a policy, follow Get started with Enterprise Management.
The whole language in one block. Every field, every capability, and every setting key appears once, with its value form or a comment that says what it takes. It is a map of the language, not a policy to publish; the setting values are placeholders. The sections that follow explain each part.
version: "0.1" # optional; read but not checked metadata: # optional; notes for you, ignored by the service name: "Engineering policy" rules: # optional; a list. See How rules combine - id: rule-name # optional; lower-case, digits, hyphens; unique capability: model_use # required; a capability or group below effect: allow # required; allow, deny, or ask. deny always wins match: ["claude-*"] # optional; pattern list. Omit to cover everything exclude: ["claude-opus-5"] # optional; pattern list carved out of match when: 'groups in ["<group id>"]' # optional; single-quoted. Omit for all # capability match takes can ask no rule # fs_read, fs_write file path, **/.env yes ungoverned # shell command, "git push *" yes ungoverned # web_search nothing yes ungoverned # web_fetch domain, *.example.com yes ungoverned # mcp server/tool, corp-tools/* yes ungoverned # model_use model identifier, claude-opus-* no denied # apikey_create nothing no denied # messaging_channel channel id, slack no denied # messaging nothing no denied # permission_mode Kiro Crew mode, yolo no yolo denied # cron nothing no Kiro Crew default # network_egress domain, *.example.com no denied # browser_use nothing no Kiro Crew default # tailnet_origin nothing no Kiro Crew default # theme_install nothing no Kiro Crew default # groups: all > builtin > filesystem. A rule on a group covers every # capability under it # when: groups in ["<group id>"] user == "<user id>" user != "<user id>" # combine with && || ! and parentheses. IDs, not names settings: # optional; a map of setting keys to values # !locked: the developer cannot change it. Every setting takes it # !replace: a default the developer can change. default_model only default_model: !replace "claude-sonnet-4.5" # a model_use rule must allow it model_governance.enabled: true # false: model rules do not limit cloud_sessions.enabled: false device_code_sign_in.enabled: true web_tools.enabled: true # rules still decide each call mcp_tools.enabled: true # rules still decide each call mcp_registry_url: "https://example.com/registry" usage_dashboard: true prompt_logging.enabled: true # needs prompt_logging.destination prompt_logging.destination: "s3://bucket/prefix/" user_activity_report.enabled: true user_activity_report.csv_export: true user_activity_report.csv_destination: "s3://bucket/prefix/" user_activity_report.otel_export: true user_activity_report.otel_secret_arn: "<secret arn>" user_activity_report.otel_protocol: grpc # grpc or http/protobuf user_activity_report.otel_temporality: Delta # Delta or Cumulative user_activity_report.otel_private_mode: privatelink # privatelink or lattice user_activity_report.otel_endpoint_service_name: "<name>" user_activity_report.otel_endpoint_service_region: us-east-1 user_activity_report.otel_resource_configuration_arn: "<arn>" user_activity_report.otel_collector_hostname: "<hostname>" realtime_event_export.enabled: true realtime_event_export.secret_arn: "<secret arn>" # a different secret realtime_event_export.private_mode: lattice # privatelink or lattice realtime_event_export.endpoint_service_name: "<name>" realtime_event_export.endpoint_service_region: us-east-1 realtime_event_export.resource_configuration_arn: "<arn>" realtime_event_export.collector_hostname: "<hostname>" crew_sandbox_min_level: standard # off, standard, cc, strict
Patterns: * matches any characters, ** matches any folders, matching is case-sensitive, and "git push *" also matches bare git push. Model identifiers have no anthropic. prefix. A misspelled optional field such as matches: gives no error and the rule then covers everything.
version: "0.1" # optional; read but not checked metadata: # optional; notes for you, ignored by the service name: "My policy" rules: # optional; a list of rules - id: model-use-allow capability: model_use effect: allow settings: # optional; a map of setting keys to values default_model: !replace "claude-sonnet-4.5"
| Key | Required | What it holds |
|---|---|---|
version | No | A string such as "0.1". Read but not checked. |
metadata | No | A map of notes for you, such as name. The service ignores it. |
rules | No | A list of rules. See Rules. |
settings | No | A map of setting keys to values. See Settings. |
The file must be a YAML map. Unknown top-level keys are ignored without an error. A policy can hold several KGL files; the service reads them as one set.
rules: - id: my-rule # optional name capability: model_use # required effect: deny # required: allow, deny or ask match: ["claude-opus-*"] # optional: what the rule covers exclude: ["claude-opus-5"] # optional: carve-outs when: 'groups in ["<group id>"]' # optional: who it applies to
| Field | Required | What it does |
|---|---|---|
id | No | A name for the rule: lower-case letters, digits, and hyphens. Must be unique per rule. |
capability | Yes | The permission the rule decides, such as model_use or shell, or a group such as builtin. |
effect | Yes | allow, deny, or ask. Some capabilities cannot ask. |
match | No | A list of patterns the rule covers. Leave it out to cover everything. |
exclude | No | A list of patterns carved out of match. |
when | No | A condition on groups or users. Leave it out to apply to everyone. |
match and exclude are always lists.
A capability with no rule is denied. Some capabilities are ungoverned instead: Kiro applies its own default until a rule names them. For those, the developer's own configuration decides, and Kiro asks when it neither allows nor denies. Any rule on an ungoverned capability, a deny included, brings back the default deny for everything the rule does not cover. Five Kiro Crew capabilities keep Kiro Crew's own default instead, as the table shows. A deny always wins. Rule order has no effect.
| With no rule | Capabilities |
|---|---|
| Denied | model_use, apikey_create, and the Kiro Crew capabilities messaging_channel, messaging, network_egress |
| Ungoverned | fs_read, fs_write, shell, web_search, web_fetch, mcp |
| Kiro Crew default | cron: scheduled jobs run until a rule denies them. permission_mode: yolo is off; normal, trust, and trust_reads stay available unless a rule denies them. browser_use, tailnet_origin, theme_install: on until a rule denies them |
So a policy with one shell deny rule turns every other shell command from ask to denied. Add an ask or allow rule beside the deny to keep the rest open.
| Capability | What it covers | match takes | Can ask |
|---|---|---|---|
fs_read | Reading and searching files, directories, and code | File path, such as .env or */secrets/* | Yes |
fs_write | Creating, editing, moving, and deleting files | File path | Yes |
shell | Running shell commands | Command, such as rm -rf * | Yes |
web_search | Searching the web | Nothing | Yes |
web_fetch | Fetching content from a web URL | Domain, such as *.example.com | Yes |
mcp | Calling tools exposed by MCP servers | server/tool, such as corp-tools/* | Yes |
model_use | Which models the agent may invoke | Model identifier, such as claude-opus-* | No |
apikey_create | Generating API keys to run Kiro CLI in automation | Nothing | No |
messaging_channel | Which messaging channels (Slack, Discord, Telegram, and others) a Kiro Crew agent may connect to and send on | Channel id, such as slack | No |
messaging | Sending messages to people from a Kiro Crew agent | Nothing | No |
permission_mode | Which tool approval modes a Kiro Crew user may select. Denying yolo turns auto-approve off, including a grant that is already on; normal, trust, and trust_reads stay available | Mode, such as yolo | No |
cron | Whether Kiro Crew may run scheduled jobs. They run until a rule denies them. A deny stops every scheduled job, including ones created earlier (they are kept), and stops new ones from being created. To turn them back on, allow the capability; removing the deny is not enough | Nothing | No |
network_egress | Hosts a Kiro Crew agent may connect to. Kiro Crew applies it to its fetch tool | Domain, such as *.example.com | No |
browser_use | Whether a Kiro Crew agent may drive its web browser (open pages, click, type). A deny turns the browser tool off. web_fetch, web_tools.enabled, and network_egress do not cover it | Nothing | No |
tailnet_origin | Whether a Kiro Crew dashboard may be opened from other devices on the user's Tailscale tailnet. A deny turns Tailscale access off and refuses turning it back on | Nothing | No |
theme_install | Whether a Kiro Crew user may download and install third-party dashboard themes. A deny refuses new installs; built-in and already installed themes keep working | Nothing | No |
Three groups also take a rule, and a rule on a group covers every capability under it: all holds everything; builtin sits under all and holds the built-in tools; filesystem sits under builtin and holds fs_read and fs_write.
A capability that takes nothing cannot have match or exclude; Publish returns BINARY_CAPABILITY_SCOPED. For which capabilities are enforced, and by which client, see Enforcement status, and for rule behaviors that are under investigation, see Known issues.
*matches any characters.claude-*matches every Claude model, including models that Kiro adds later."git push *"also matches the bare commandgit push.- File paths use globs.
**matches any folders, so**/secrets/**matches every file under anysecretsfolder. - Patterns are case-sensitive. A literal
*cannot be matched. - Model identifiers have no
anthropic.prefix.anthropic.claude-opus-*matches nothing; writeclaude-opus-*.
rules: - capability: web_search effect: allow when: 'groups in ["12345678-90ab-cdef-1234-567890abcdef"]' - capability: web_search effect: deny when: 'user == "abcdef12-3456-7890-abcd-ef1234567890"' - capability: shell effect: ask match: ["git push *"] when: '!(groups in ["<group id>"]) && user != "<user id>"'
- Use user IDs and group IDs, not names. In YAML you type the ID; in the Form view you pick the user or group and the console writes the ID. With Okta, the Form view is the only place to get the ID. Publish checks each ID against your directory. To find the IDs, see Find user and group IDs.
- Always put the whole value in single quotes.
- Combine conditions with
&&,||,!, and parentheses.
settings: default_model: !locked "claude-sonnet-4.5" prompt_logging.enabled: true prompt_logging.destination: "s3://my-bucket/prompts/"
| Tag | Meaning | Allowed on |
|---|---|---|
!locked | The developer cannot change the value. The Form view shows this as Developer override: Not allowed. | Every setting |
!replace | A default the developer can change. The Form view shows this as Developer override: Can update. | default_model only |
default_modelmust be a model that amodel_userule allows; otherwise Publish returnsPOLICY_BOUNDARY_VIOLATION. Its shipped default isauto.model_governance.enableddecides whethermodel_userules limit the models developers see. A new profile ships with itfalse; set it totruefor the rules to take effect.- A setting that is turned on may need another one.
prompt_logging.enabled: trueneedsprompt_logging.destination; otherwise Publish returnsSETTING_REQUIREMENT_MISSING. The console shows no error for a missing pair until you publish, so set both values.
For every setting key, its shipped default, and its values, see Setting keys.
version: "0.1" rules: - id: model-use-allow capability: model_use effect: allow match: ["claude-*"] - id: models-deny-opus-for-contractors capability: model_use effect: deny match: ["claude-opus-*"] when: 'groups in ["<group id>"]' - id: web-search-ask capability: web_search effect: ask - id: shell-ask capability: shell effect: ask - id: shell-deny-destructive capability: shell effect: deny match: ["rm -rf *", "sudo *"] settings: default_model: !locked "claude-sonnet-4.5"
This policy allows Claude models, but not Opus for the group named in when. Kiro asks before web searches and shell commands, and refuses destructive commands. Capabilities it does not name keep Kiro's own default. For more policies, from an eight-rule floor to a complete one, see KGL examples.
Publish returns these codes. The API returns them in the code field of a diagnostic; see EnterpriseManagementDiagnostic.
| Code | Cause |
|---|---|
YAML_PARSE_ERROR | The file is not valid YAML. |
UNKNOWN_CAPABILITY / UNKNOWN_SETTING | A typo in a capability or setting name. |
DECISION_NOT_ALLOWED | ask on a capability that cannot ask. |
BINARY_CAPABILITY_SCOPED | match on a capability that takes nothing. |
FIELD_NOT_A_STRING_LIST | match: "x" in place of match: ["x"]. |
WHEN_PARSE_ERROR | Bad when syntax, or the value is not quoted. |
UNKNOWN_GROUP / UNKNOWN_USER | A name or a wrong ID in when. |
POLICY_BOUNDARY_VIOLATION | default_model is not allowed by a model_use rule. |
SETTING_REQUIREMENT_MISSING | A setting that is on needs another value. |
A misspelled optional field, such as matches:, gives no error. The service ignores it, and the rule then covers everything. Check the spelling of every optional field before you publish.
Kiro Governance Language (KGL) reference