Structured policy schema
A structured policy is the unit of authorization in Wicket. You edit policies in the dashboard’s policy builder; Wicket compiles them to Cedar for evaluation. This page documents the policy fields. The resource-condition catalog below covers every service that has conditions, listing each condition type by name; the per-service value rules are summarized rather than exhaustively enumerated. For the reasoning behind the structured approach, see Why structured policies.
Core fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Display name, shown in the policy list and audit entries |
service | string | yes | Connector this policy applies to: github, slack, linear, … (one service per policy) |
effect | permit | forbid | yes | Whether matching calls are allowed or blocked |
tools | string[] | yes | Tool names the policy covers, e.g. delete_file, merge_pull_request (unprefixed) |
principal | object | yes | Who the policy applies to — see Principal scope |
enabled | boolean | yes | Disabled policies are ignored at evaluation time |
denyMessage | string | no | Human-readable reason shown with denials. Max 500 characters. Only meaningful on forbid policies |
Evaluation defaults: a call is denied unless a permit policy matches, and a matching forbid always wins over a matching permit.
Principal scope
{ "type": "all_members" }{ "type": "specific_members", "userIds": ["<user-id>", "..."] }| Value | Effect |
|---|---|
all_members | Applies to every member of the agent |
specific_members | Applies only to the listed members — pick them in the builder |
Time constraints
All sub-fields optional; omitted fields mean “no restriction on that axis”.
| Field | Type | Description |
|---|---|---|
daysOfWeek | number[] | Allowed days, 0 = Sunday … 6 = Saturday. Empty/absent = all days |
hoursFrom | number | Start hour, 0–23 inclusive. Absent = 0 |
hoursTo | number | End hour, 0–23 inclusive. Absent = 23 |
activeFrom | ISO date | Policy activation date |
activeTo | ISO date | Policy expiration date (inclusive) |
Repository, channel, and page restrictions
Scope a policy to specific resources, picked from your harvested entities:
| Field | Service | Shape | Meaning |
|---|---|---|---|
repoRestrictions | GitHub | { owner, repo? }[] | Restrict to repos. Omit repo to match any repo under the owner/org. Empty/absent = no restriction. Max 20 entries |
channelRestrictions | Slack | { channelId }[] | Restrict to specific channels, chosen from the channel picker. Max 20 entries |
notionPageRestrictions | Notion | { pageId }[] | Restrict to specific Notion pages. Empty/absent = no restriction. Max 20 entries. Cannot be combined with a database or block notionResourceScope |
Resource conditions
Resource conditions match attributes of the resource a tool call targets. Multiple conditions of the same type OR together where noted; different types combine according to resourceConditionMatch.
| Field | Values | Meaning |
|---|---|---|
resourceConditionMatch | all (default) | any | Outer match across resource-condition groups: all = every group must hold (AND), any = at least one (OR). Applies to resource conditions only — principal, time, session, and repo/channel/page restrictions always AND. Auto-locks to any when two or more concrete resource kinds are active (a resource is exactly one kind, so AND across kinds is unsatisfiable) |
githubResourceScope | any | repository | pull_request | issue | branch | release | GitHub only: restrict the policy to one resource kind. Absent/any = no kind guard |
notionResourceScope | page | database | block | Notion only: restrict the policy to one resource kind. Absent = no kind guard. Each scope only permits its applicable condition types (e.g. block scope excludes notionDatabaseId and notionIsPublic) |
resourceKinds | string[] | Concrete resource kinds explicitly activated in the builder (a tab toggled on, even with no conditions). Drives multi-kind coexistence. Absent = derived from githubResourceScope/notionResourceScope |
resourceConditionInnerMatch | Record<kind, "all" | "any"> | Inner match within each per-kind condition group. Key is the resource kind (common for the kind-agnostic group). Missing key = all. The outer resourceConditionMatch then combines these groups |
Condition types by service
The catalog below lists, by service, the resource-condition type names available. Boolean conditions match true/false; string conditions match the exact harvested value; enum conditions are listed with their allowed values.
GitHub — repository attributes
| Type | Value | Matches |
|---|---|---|
visibility | public | private | internal | Repository visibility |
archived | boolean | Archived state |
topic | string | Repository topic |
GitHub — pull request / issue attributes
| Type | Value | Matches |
|---|---|---|
state | open | closed | merged | PR/issue state |
hasLabel | string | Label name present |
isDraft | boolean | Draft PR |
isLocked | boolean | Locked conversation |
GitHub — branch / release attributes
| Type | Value | Matches |
|---|---|---|
isProtected | boolean | Protected branch |
isDefaultBranch | boolean | Default branch |
isPrerelease | boolean | Pre-release |
GitHub — entity pickers (values OR together within a type)
| Type | Value format |
|---|---|
pullRequest | owner/repo#N |
issueRef | owner/repo#N |
branchRef | owner/repo@ref |
releaseRef | owner/repo@ref |
Slack — channel attributes
| Type | Value |
|---|---|
isPrivate | boolean |
isArchived | boolean |
isShared | boolean |
Linear — issue attributes and pickers
| Type | Value |
|---|---|
issueStateType | backlog | unstarted | started | completed | canceled |
issuePriority | integer 0–4 (0 = no priority, 1 = urgent … 4 = low) |
issueHasLabel | label name (manual match) |
issueHasLabelId | harvested label ID (stable match) |
issueTeamId | team ID (picker; values OR together) |
issueProjectId | project ID (picker; values OR together) |
issueId | issue ID (picker; values OR together) |
Vercel — project / deployment attributes
| Type | Value |
|---|---|
vercelTarget | production | preview | staging |
deploymentState | string |
vercelVisibility | public | private |
HubSpot — CRM object attributes
| Type | Value |
|---|---|
lifecycleStage | string |
objectArchived | boolean |
Stripe — account / customer attributes
| Type | Value |
|---|---|
customerDelinquent | boolean |
livemode | boolean |
Notion — page / database / block attributes
| Type | Value |
|---|---|
notionArchived | boolean |
notionInTrash | boolean |
notionDatabaseId | database ID (entity equality) |
notionParentId | parent entity ID |
notionParentType | page | database | block | workspace |
notionIsPublic | boolean |
Sentry — issue / project attributes
| Type | Value |
|---|---|
issueLevel | string |
issueStatus | resolved | unresolved | ignored |
isPublicProject | boolean |
Hugging Face — repository attributes
| Type | Value |
|---|---|
repoType | model | dataset | space |
repoPrivate | boolean |
repoGated | boolean |
Supabase — project / function / bucket / branch attributes
| Type | Value |
|---|---|
projectStatus | string |
region | string |
projectCloudProvider | string |
projectInfraComputeSize | string |
projectIsBranchEnabled | boolean |
projectHighAvailability | boolean |
projectIsHibernating | boolean |
functionStatus | string |
functionVerifyJwt | boolean |
bucketPublic | boolean |
branchIsDefault | boolean |
branchPersistent | boolean |
branchStatus | string |
PlanetScale — database attributes
| Type | Value |
|---|---|
dbState | string |
Google / Gmail — message attributes
| Type | Value |
|---|---|
gmailHasLabel | label name |
gmailIsUnread | boolean |
gmailFromDomain | string |
gmailFromAddress | string |
gmailToDomain | string |
gmailReceivedSince | number (timestamp) |
Atlassian — Jira issue attributes
| Type | Value |
|---|---|
atlassianIssueStatusCategory | todo | inprogress | done |
atlassianIssueType | string |
atlassianIssuePriority | string |
Airtable — entity selections
| Type | Value |
|---|---|
airtableBase | base ID |
airtableTable | table ID |
Network conditions
Network conditions match the source IP of the caller that issued the tool call. They are held in networkConditions, an array of IP conditions. Each entry has the shape:
{ "mode": "exact", "values": ["203.0.113.7"], "negate": false }{ "mode": "range", "values": ["10.0.0.0/8", "192.168.0.0/16"], "negate": false }| Field | Values | Meaning |
|---|---|---|
mode | exact | range | exact matches a bare IP address; range matches a CIDR block via Cedar’s isInRange |
values | string[] | One or more IPs (exact) or CIDR ranges (range). Multiple values within one condition OR together |
negate | boolean (optional) | When true, the condition is inverted — the call matches when the source IP is not in the set |
Rules and limits:
exactmode requires bare IP addresses (no/);rangemode requires CIDR notation (must include/).- At most 20 network conditions per policy, and at most 20 values per condition.
- Multiple conditions combine with the rest of the policy as an AND (alongside principal, time, and resource scope).
Session conditions
Session conditions make a policy depend on what already happened in the same tool session (one MCP client conversation). See Tool sessions for the session model.
Tool condition — matches when a prior tool was used in this session:
{ "kind": "tool", "service": "github", "tool": "get_file_contents", "minCount": 3 }| Field | Description |
|---|---|
service | Service of the prior tool |
tool | Tool name |
minCount | Minimum times it was called (default 1) |
Policy condition — matches based on how often another policy already fired in this session:
{ "kind": "policy", "policyKey": "<policy-key>", "decisionBucket": "deny", "operator": "gte", "minCount": 3 }| Field | Description |
|---|---|
policyKey | The other policy’s key |
decisionBucket | allow or deny — which decisions to count |
operator | lt, lte, gt, gte, eq (default gte) |
minCount | Count threshold |
Example use: forbid all tools for the rest of a session after a policy has been denied 3 times — an automatic circuit breaker for misbehaving agents.
Read-only fields
| Field | Description |
|---|---|
policyKey | Stable identifier used in Cedar output, audit entries, and session policy conditions |
triggerCount | How many times this policy matched a call |
lastTriggered | Timestamp of the most recent match |
cedarPolicy | The compiled Cedar text (visible via Preview) |
createdAt / updatedAt | Timestamps |
Policy versions
Every save creates a version with:
- A snapshot of the full structured policy
- The compiled Cedar text
- Author and timestamp
- Change type:
create,update,toggle, ordelete
The dashboard’s Versions view shows a structured field-by-field diff between any two versions (tools added/removed, time window changes, condition changes, deny message changes). Audit entries record the exact policy version that matched a call, so you can always answer “what did the policy say at the time?”
Complete examples
The authoring shape — what you submit on create/update. Wicket adds the server-managed fields (id, policyKey, triggerCount, cedarPolicy, timestamps).
Forbid destructive GitHub tools, with a deny message:
{ "name": "Block GitHub destructive tools", "service": "github", "effect": "forbid", "tools": ["delete_file", "merge_pull_request", "create_repository", "fork_repository"], "principal": { "type": "all_members" }, "enabled": true, "denyMessage": "Destructive GitHub operations are blocked. Ask in #platform if you need this."}Permit writes during business hours only (Mon–Fri, 09:00–17:00):
{ "name": "Business-hours GitHub writes", "service": "github", "effect": "permit", "tools": ["create_pull_request", "create_or_update_file", "push_files", "add_issue_comment"], "principal": { "type": "all_members" }, "enabled": true, "timeConstraints": { "daysOfWeek": [1, 2, 3, 4, 5], "hoursFrom": 9, "hoursTo": 17 }}Read-only access scoped to one org’s public, non-archived repos:
{ "name": "Public repos in my-org, read only", "service": "github", "effect": "permit", "tools": ["get_file_contents", "search_code", "list_issues", "list_commits"], "principal": { "type": "specific_members", "userIds": ["u_7f3k…"] }, "enabled": true, "repoRestrictions": [{ "owner": "my-org" }], "resourceConditions": [ { "type": "visibility", "value": "public" }, { "type": "archived", "value": false } ], "resourceConditionMatch": "all"}Session circuit breaker — lock the session after 3 denials of the guard policy:
{ "name": "Circuit breaker", "service": "github", "effect": "forbid", "tools": ["*all tools selected in the builder*"], "principal": { "type": "all_members" }, "enabled": true, "sessionConditions": [ { "kind": "policy", "policyKey": "block-github-destructive-tools", "decisionBucket": "deny", "operator": "gte", "minCount": 3 } ], "denyMessage": "Session locked after repeated denied attempts. Ask an owner to reset your session."}Related
- Author your first policy — guided walkthrough
- Policy recipes — copy-paste patterns
- Preview and simulation — test before enforcing