---
title: "Policies (PolicyDecl)"
description: "Generated from the doc schema (CatalogDocSchema, @canonify/kernel-catalog/doc): each field’s type, default and description are the schema’s own, and each example is a bundle file that readBundle…"
order: 10
id: "model/policy"
---

# Policies (PolicyDecl)

Generated from the doc schema (`CatalogDocSchema`, @canonify/kernel-catalog/doc): each field’s type, default and description are the schema’s own, and each example is a bundle file that readBundle decodes. A field’s Example is its value in one of those examples.

## PolicyDecl {#policydecl}

A policy: who may do what, on which rows and columns (authz).

The `policies` slot of the doc holds these, keyed by id. In a bundle each is one file, `policies/<name>.json`, named by its `name`, which the file then leaves out.

One of, told apart by `mode`:

- [PolicyDecl.permit](policy.md#policydecl.permit)
- [PolicyDecl.restrict](policy.md#policydecl.restrict)
- [PolicyDecl.deny](policy.md#policydecl.deny)

## PolicyDecl.permit {#policydecl.permit}

A permit: grants the operations (the union of every matching permit). A permit adds to the role baselines; it never narrows them. To narrow, write a restrict or a deny.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `id` | [PolicyId](shapes.md#policyid) | yes (propose assigns it; a new declaration leaves it out) | — | — | A policy id (`po_…`); renames keep it. |
| `name` | [Ident](shapes.md#ident) | yes | — | `"members_own_orders"` | The policy name. |
| `label` | [Text](shapes.md#text) | yes | — | `"Members see their own orders"` | The words shown for it. |
| `subject` | [Subject](policy.md#subject) | yes | — | `{"role":"member"}` | Who it applies to. |
| `on` | [ResourceSel](policy.md#resourcesel) | yes | — | `{"types":["ty_3f8v6h2n0c"]}` | What it is about. |
| `ops` | [AccessOp](policy.md#accessop)[] (at least one) | yes | — | `["read","update"]` | The operations it is about. |
| `reason` | [MsgRef](shapes.md#msgref) | no | — | `{"text":"You can only work on your own orders."}` | What a refusal or a “why” shows for it. |
| `mode` | `"permit"` | yes | — | `"permit"` | permit: it grants the operations on the rows and columns it covers. |
| `rows` | [Expr](shapes.md#expr) | no | — | `"owner == me.member"` | A CX condition over the type’s row and `me`: the rows it covers. Unknown on a row = not covered; absent = every row. |
| `check` | [Expr](shapes.md#expr) | no | — | `"owner == me.member"` | A CX condition the row must hold after a create, update or transition (WITH CHECK); absent = the same as rows. |
| `columns` | [ColumnSel](policy.md#columnsel) | no | — | `{"mode":"mask","props":["pr_8b2f4h6j0k"]}` | The columns it shows, masks or hides; absent = every column. |

`policies/members_own_orders.json`

```json
{
  "label": "Members see their own orders",
  "mode": "permit",
  "subject": { "role": "member" },
  "on": { "types": ["ty_3f8v6h2n0c"] },
  "ops": ["read", "update"],
  "rows": "owner == me.member",
  "check": "owner == me.member",
  "columns": { "mode": "mask", "props": ["pr_8b2f4h6j0k"] },
  "reason": { "text": "You can only work on your own orders." }
}
```

## PolicyDecl.restrict {#policydecl.restrict}

A restrict: a wall on rows, which every grant must also pass.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `id` | [PolicyId](shapes.md#policyid) | yes (propose assigns it; a new declaration leaves it out) | — | — | A policy id (`po_…`); renames keep it. |
| `name` | [Ident](shapes.md#ident) | yes | — | `"drivers_own_region"` | The policy name. |
| `label` | [Text](shapes.md#text) | yes | — | `"Drivers stay in their region"` | The words shown for it. |
| `subject` | [Subject](policy.md#subject) | yes | — | `{"group":"gr_5r7t9w1y3a"}` | Who it applies to. |
| `on` | [ResourceSel](policy.md#resourcesel) | yes | — | `{"types":["ty_3f8v6h2n0c"]}` | What it is about. |
| `ops` | [AccessOp](policy.md#accessop)[] (at least one) | yes | — | `["read"]` | The operations it is about. |
| `reason` | [MsgRef](shapes.md#msgref) | no | — | `{"text":"Drivers work only in the north region."}` | What a refusal or a “why” shows for it. |
| `mode` | `"restrict"` | yes | — | `"restrict"` | restrict: a wall; whatever else grants the operations, only the rows it covers. |
| `rows` | [Expr](shapes.md#expr) | no | — | `"region == \"north\" \|\| lines.any(l => l.qty > 0)"` | A CX condition over the type’s row and `me`: the rows it covers. Unknown on a row = not covered; absent = every row. |
| `check` | [Expr](shapes.md#expr) | no | — | `"region == \"north\""` | A CX condition the row must hold after a create, update or transition (WITH CHECK); absent = the same as rows. |
| `except` | [Subject](policy.md#subject)[] (at least one) | no | — | `[{"member":"u-01HZY8Q2:person"}]` | The actors it does not bind: an actor any of these names (at its standing and its stored standing alike) passes it. Absent = it binds every actor its subject names. |
| `direct` | `true` | no | — | `true` | true: it binds only writes made under the invoker’s own rights; an action that writes with its own rights (writeRights: action) passes it. Only on types and write ops. |

`policies/drivers_own_region.json`

```json
{
  "label": "Drivers stay in their region",
  "mode": "restrict",
  "subject": { "group": "gr_5r7t9w1y3a" },
  "on": { "types": ["ty_3f8v6h2n0c"] },
  "ops": ["read"],
  "rows": "region == \"north\" || lines.any(l => l.qty > 0)",
  "check": "region == \"north\"",
  "reason": { "text": "Drivers work only in the north region." }
}
```

`policies/drivers_ship_north_themselves.json`

```json
{
  "label": "Drivers ship north orders themselves",
  "mode": "restrict",
  "subject": { "role": "member" },
  "on": { "types": ["ty_3f8v6h2n0c"] },
  "ops": [{ "transition": "tn_6s8v0x2z4b" }],
  "rows": "region == \"north\"",
  "except": [{ "member": "u-01HZY8Q2:person" }],
  "direct": true
}
```

## PolicyDecl.deny {#policydecl.deny}

A deny: a bar on the resource or on some of its columns.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `id` | [PolicyId](shapes.md#policyid) | yes (propose assigns it; a new declaration leaves it out) | — | — | A policy id (`po_…`); renames keep it. |
| `name` | [Ident](shapes.md#ident) | yes | — | `"drivers_no_money"` | The policy name. |
| `label` | [Text](shapes.md#text) | yes | — | `"Drivers never see money"` | The words shown for it. |
| `subject` | [Subject](policy.md#subject) | yes | — | `{"group":"gr_5r7t9w1y3a"}` | Who it applies to. |
| `on` | [ResourceSel](policy.md#resourcesel) | yes | — | `{"types":["ty_3f8v6h2n0c"]}` | What it is about. |
| `ops` | [AccessOp](policy.md#accessop)[] (at least one) | yes | — | `["read"]` | The operations it is about. |
| `reason` | [MsgRef](shapes.md#msgref) | no | — | `{"text":"Prices are for the office."}` | What a refusal or a “why” shows for it. |
| `mode` | `"deny"` | yes | — | `"deny"` | deny: a bar; the operations are refused, on the whole resource or on the columns it names. |
| `columns` | [ColumnSel](policy.md#columnsel) | no | — | `{"mode":"hide","props":["pr_8b2f4h6j0k"]}` | The columns it bars; absent = the whole resource. |
| `except` | [Subject](policy.md#subject)[] (at least one) | no | — | `[{"group":"gr_5r7t9w1y3a"}]` | The actors it does not bind: an actor any of these names (at its standing and its stored standing alike) passes it. Absent = it binds every actor its subject names. |
| `direct` | `true` | no | — | `true` | true: it binds only writes made under the invoker’s own rights; an action that writes with its own rights (writeRights: action) passes it. Only on types and write ops. |

`policies/drivers_no_money.json`

```json
{
  "label": "Drivers never see money",
  "mode": "deny",
  "subject": { "group": "gr_5r7t9w1y3a" },
  "on": { "types": ["ty_3f8v6h2n0c"] },
  "ops": ["read"],
  "columns": { "mode": "hide", "props": ["pr_8b2f4h6j0k"] },
  "reason": { "text": "Prices are for the office." }
}
```

`policies/members_order_through_actions.json`

```json
{
  "label": "Members change orders only through actions",
  "mode": "deny",
  "subject": { "role": "member" },
  "on": { "types": ["ty_3f8v6h2n0c"] },
  "ops": ["create", "update", "delete"],
  "except": [{ "group": "gr_5r7t9w1y3a" }],
  "direct": true
}
```

`policies/only_drivers_place_orders.json`

```json
{
  "label": "Only drivers place orders",
  "mode": "deny",
  "subject": { "role": "member" },
  "on": { "actions": ["ac_3h5k7n9q1s"] },
  "ops": [{ "invoke": "ac_3h5k7n9q1s" }],
  "except": [{ "group": "gr_5r7t9w1y3a" }]
}
```

## Subject {#subject}

Who it names: a role, a group, a member, the visitors of an app or form, everyone, or no one (a credential narrowing) (authz Subject, C56).

One of:

- [Subject.role](policy.md#subject.role)
- [Subject.group](policy.md#subject.group)
- [Subject.member](policy.md#subject.member)
- [Subject.carrier](policy.md#subject.carrier)
- [Subject.everyone](policy.md#subject.everyone)
- [Subject.narrowing](policy.md#subject.narrowing)

## Subject.role {#subject.role}

The members who hold a role.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `role` | `"owner"` \| `"admin"` \| `"member"` \| `"viewer"` | yes | — | `"admin"` | The role: its holders, by the role the Forspring account gives them in the org. |

The `subject` of a policy file (`policies/<name>.json`), or an approval’s approvers.

```json
{ "role": "admin" }
```

## Subject.group {#subject.group}

The members of a group (membership is data: the member list).

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `group` | [GroupId](shapes.md#groupid) | yes | — | `"gr_5r7t9w1y3a"` | The group. |

The `subject` of a policy file (`policies/<name>.json`), or an approval’s approvers.

```json
{ "group": "gr_5r7t9w1y3a" }
```

## Subject.member {#subject.member}

One member.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `member` | string (matching `^[\x21-\x7e]{1,255}$`) | yes | — | `"u-01HZY8Q2:person"` | The member, by their Forspring account id. |

The `subject` of a policy file (`policies/<name>.json`), or an approval’s approvers.

```json
{ "member": "u-01HZY8Q2:person" }
```

## Subject.carrier {#subject.carrier}

The visitors of a published app or form (carriers; never org members).

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `carrier` | [CarrierArtifact](policy.md#carrierartifact) | yes | — | `{"app":"ap_6c8e0g2j4m"}` | The app or public form whose visitors it names. |
| `persona` | [PersonaId](shapes.md#personaid) | no | — | `"pe_0b2d4f6h8k"` | Only visitors with this persona; absent = every visitor of the artifact. |

The `subject` of a policy file (`policies/<name>.json`), or an approval’s approvers.

```json
{ "carrier": { "app": "ap_6c8e0g2j4m" }, "persona": "pe_0b2d4f6h8k" }
```

## Subject.everyone {#subject.everyone}

Every actor of every kind.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `everyone` | `true` | yes | — | `true` | Every actor: members, services, carriers and declarations. |

The `subject` of a policy file (`policies/<name>.json`), or an approval’s approvers.

```json
{ "everyone": true }
```

## Subject.narrowing {#subject.narrowing}

A credential narrowing: grants no actor by itself; a capped key holds only what its narrowing permits name (authz.md §2.2 step 7).

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `narrowing` | `true` | yes | — | `true` | No actor: the permit narrows only the account keys whose DO-side cap names it. |

The `subject` of a policy file (`policies/<name>.json`), or an approval’s approvers.

```json
{ "narrowing": true }
```

## CarrierArtifact {#carrierartifact}

A published artifact: an app or a public form.

One of:

- [CarrierArtifact.app](policy.md#carrierartifact.app)
- [CarrierArtifact.form](policy.md#carrierartifact.form)

## CarrierArtifact.app {#carrierartifact.app}

A published app (internal, external or public).

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `app` | [AppId](shapes.md#appid) | yes | — | `"ap_6c8e0g2j4m"` | The app. |

The `carrier` of a policy’s subject.

```json
{ "app": "ap_6c8e0g2j4m" }
```

## CarrierArtifact.form {#carrierartifact.form}

A public form.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `form` | [FormId](shapes.md#formid) | yes | — | `"fm_4n6q8s0v2x"` | The public form. |

The `carrier` of a policy’s subject.

```json
{ "form": "fm_4n6q8s0v2x" }
```

## ResourceSel {#resourcesel}

What it is about: types, actions, views, apps, forms, platform data, the catalog or the org.

One of:

- [ResourceSel.types](policy.md#resourcesel.types)
- [ResourceSel.actions](policy.md#resourcesel.actions)
- [ResourceSel.views](policy.md#resourcesel.views)
- [ResourceSel.apps](policy.md#resourcesel.apps)
- [ResourceSel.forms](policy.md#resourcesel.forms)
- [ResourceSel.platform](policy.md#resourcesel.platform)
- [ResourceSel.catalog](policy.md#resourcesel.catalog)
- [ResourceSel.org](policy.md#resourcesel.org)

## ResourceSel.types {#resourcesel.types}

Rows of types: the only resource a row rule or a column set applies to.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `types` | [TypeId](shapes.md#typeid)[] (at least one) | yes | — | `["ty_3f8v6h2n0c"]` | The types, by id. |

The `on` of a policy file.

```json
{ "types": ["ty_3f8v6h2n0c", "ty_7k2m9q4z1a"] }
```

## ResourceSel.actions {#resourcesel.actions}

Actions.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `actions` | [ActionId](shapes.md#actionid)[] (at least one) | yes | — | `["ac_3h5k7n9q1s"]` | The actions, by id. |

The `on` of a policy file.

```json
{ "actions": ["ac_3h5k7n9q1s"] }
```

## ResourceSel.views {#resourcesel.views}

Views.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `views` | [ViewId](shapes.md#viewid)[] (at least one) | yes | — | `["vw_1m3p5r7t9w"]` | The views, by id. |

The `on` of a policy file.

```json
{ "views": ["vw_1m3p5r7t9w"] }
```

## ResourceSel.apps {#resourcesel.apps}

Apps.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `apps` | [AppId](shapes.md#appid)[] (at least one) | yes | — | `["ap_6c8e0g2j4m"]` | The apps, by id. |

The `on` of a policy file.

```json
{ "apps": ["ap_6c8e0g2j4m"] }
```

## ResourceSel.forms {#resourcesel.forms}

Public forms.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `forms` | [FormId](shapes.md#formid)[] (at least one) | yes | — | `["fm_4n6q8s0v2x"]` | The public forms, by id. |

The `on` of a policy file.

```json
{ "forms": ["fm_4n6q8s0v2x"] }
```

## ResourceSel.platform {#resourcesel.platform}

Platform data: the audit, record history, the member list, keys or files.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `platform` | (`"audit"` \| `"history"` \| `"members"` \| `"keys"` \| `"files"`)[] (at least one) | yes | — | `["audit","history"]` | The platform relations. |

The `on` of a policy file.

```json
{ "platform": ["audit", "history"] }
```

## ResourceSel.catalog {#resourcesel.catalog}

The catalog itself (manage_catalog, manage_policy).

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `catalog` | `true` | yes | — | `true` | The org’s catalog. |

The `on` of a policy file.

```json
{ "catalog": true }
```

## ResourceSel.org {#resourcesel.org}

The org as a whole (members, keys, export, audit, break-glass, erase).

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `org` | `true` | yes | — | `true` | The org. |

The `on` of a policy file.

```json
{ "org": true }
```

## AccessOp {#accessop}

An operation it allows or refuses.

One of:

- [AccessOp.set](policy.md#accessop.set)
- [AccessOp.attach](policy.md#accessop.attach)
- [AccessOp.transition](policy.md#accessop.transition)
- [AccessOp.invoke](policy.md#accessop.invoke)
- [AccessOp.approve](policy.md#accessop.approve)
- `"read"` | `"create"` | `"update"` | `"delete"` | `"open"` | `"manage_policy"` | `"manage_catalog"` | `"manage_members"` | `"manage_keys"` | `"export"` | `"read_audit"` | `"break_glass"` | `"pin_clock"` | `"erase"`

## AccessOp.set {#accessop.set}

Set one property of a row (a column write grant).

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `set` | [PropId](shapes.md#propid) | yes | — | `"pr_2k4m6p8r0t"` | The property. |

An item of `ops` in a policy file.

```json
{ "set": "pr_2k4m6p8r0t" }
```

## AccessOp.attach {#accessop.attach}

Attach a file to one property of a row.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `attach` | [PropId](shapes.md#propid) | yes | — | `"pr_2k4m6p8r0t"` | The file property. |

An item of `ops` in a policy file.

```json
{ "attach": "pr_2k4m6p8r0t" }
```

## AccessOp.transition {#accessop.transition}

Move a row along one lifecycle transition.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `transition` | [TransitionId](shapes.md#transitionid) | yes | — | `"tn_6s8v0x2z4b"` | The transition. |

An item of `ops` in a policy file.

```json
{ "transition": "tn_6s8v0x2z4b" }
```

## AccessOp.invoke {#accessop.invoke}

Call one action.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `invoke` | [ActionId](shapes.md#actionid) | yes | — | `"ac_3h5k7n9q1s"` | The action. |

An item of `ops` in a policy file.

```json
{ "invoke": "ac_3h5k7n9q1s" }
```

## AccessOp.approve {#accessop.approve}

Approve a pending call of one action.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `approve` | [ActionId](shapes.md#actionid) | yes | — | `"ac_3h5k7n9q1s"` | The action. |

An item of `ops` in a policy file.

```json
{ "approve": "ac_3h5k7n9q1s" }
```

## ColumnSel {#columnsel}

A set of columns and what happens to them.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `mode` | `"allow"` \| `"mask"` \| `"hide"` | yes | — | `"mask"` | allow: only these columns are shown; mask: these are shown masked; hide: these are not shown. |
| `props` | [PropId](shapes.md#propid)[] (at least one) | yes | — | `["pr_8b2f4h6j0k"]` | The properties, by id. |

The `columns` of a permit or a deny policy file.

```json
{ "mode": "allow", "props": ["pr_2k4m6p8r0t", "pr_8b2f4h6j0k"] }
```

## DeclarerRef {#declarerref}

Who declared it: an account or a platform plugin (authz DeclarerRef, C35); recorded by the service.

One of:

- [DeclarerRef.account](policy.md#declarerref.account)
- [DeclarerRef.platform](policy.md#declarerref.platform)

## DeclarerRef.account {#declarerref.account}

A member: the trigger acts with that member’s live standing.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `account` | string (matching `^[\x21-\x7e]{1,255}$`) | yes | — | `"u-01HZY8Q2:person"` | The member who promoted it, by their Forspring account id. |

The `declarer` of a trigger file (recorded by the service at promote).

```json
{ "account": "u-01HZY8Q2:person" }
```

## DeclarerRef.platform {#declarerref.platform}

A platform plugin: the trigger acts within the plugin’s declared manifest.

| Field | Type | Required | Default | Example | Description |
|---|---|---|---|---|---|
| `platform` | string (matching `^[a-z][a-z0-9_-]{0,63}$`) | yes | — | `"esign"` | The platform plugin. |

The `declarer` of a trigger file (recorded by the service at promote).

```json
{ "platform": "esign" }
```
