---
title: Policies and Permissions
path: core-concepts/policies-and-permissions
status: published
---

# Policies and Permissions

Access control in ScaiVault is policy-based. A **policy** is a list of **rules** that match paths and grant permissions. Policies are **bound** to **identities** — users, service accounts, groups. When a request comes in, ScaiVault evaluates every policy the caller is bound to and lets the action through if any rule matches.

## Policy shape

```json
{
  "id": "pol_abc123",
  "name": "production-read-only",
  "description": "Read access to production secrets for developers",
  "rules": [
    {
      "path_pattern": "environments/production/**",
      "permissions": ["read", "list"],
      "conditions": {
        "ip_ranges": ["10.0.0.0/8"],
        "require_mfa": true
      }
    },
    {
      "path_pattern": "shared/certificates/*",
      "permissions": ["read"]
    }
  ],
  "bindings": [
    {"identity_type": "group", "identity_id": "group:developers"}
  ]
}
```

Key points:

- **Multiple rules per policy.** Different paths with different permissions live in the same policy — group them by the purpose, not by the shape.
- **Permissions per rule, not per policy.** A rule grants a specific set of actions on paths matching its pattern.
- **Conditions are additional constraints.** Match the pattern *and* satisfy the conditions, or the rule doesn't apply.
- **Bindings come from ScaiKey identities.** Bind to groups when you can; individual user bindings don't scale.

## Path patterns

Patterns are glob-style:

| Pattern | Matches | Does not match |
|---------|---------|----------------|
| `app/db` | Exactly `app/db` | `app/db/password` |
| `app/db/*` | `app/db/password`, `app/db/user` | `app/db/primary/password` |
| `app/**` | `app/db`, `app/db/password`, anything under `app/` | `other/app/db` |
| `**/ssl/*` | `app/ssl/cert`, `service/ssl/key` | `ssl/cert` (no parent) |

`**` matches any number of path segments including zero. `*` matches exactly one segment. `?` matches exactly one character.

Patterns are anchored at the start of the path. A leading `/` is not permitted.

## Permissions

| Permission | What it allows |
|------------|----------------|
| `read` | Read secret values at matching paths |
| `write` | Create and update secrets at matching paths |
| `delete` | Delete secrets at matching paths |
| `list` | List/enumerate secrets at matching paths (without reading values) |
| `rotate` | Trigger rotation on secrets at matching paths |
| `admin` | All of the above |

Grant the narrowest set that does the job. A service that reads one secret should have `read` on exactly that path, nothing else.

## Conditions

Optional constraints that narrow when a rule applies. All conditions on a rule must be satisfied; otherwise the rule is as if it didn't exist for this request.

| Condition | Effect |
|-----------|--------|
| `ip_ranges` | Caller's source IP must be in one of the CIDR ranges |
| `require_mfa` | The token must have been issued with fresh MFA (less than 15 min ago) |
| `time_window.start` / `time_window.end` | Request time (UTC) must fall within the window |

Example: a rule that allows engineering to read production secrets, but only from the corporate VPN and with MFA:

```json
{
  "path_pattern": "environments/production/**",
  "permissions": ["read"],
  "conditions": {
    "ip_ranges": ["10.0.0.0/8"],
    "require_mfa": true
  }
}
```

Conditions make `access_denied` *specific*. The error tells you exactly which condition failed (`ip_not_allowed`, `mfa_required`), so you can act on it.

## Bindings

A binding ties a policy to an identity:

```json
{
  "id": "bind_xyz",
  "policy_id": "pol_abc123",
  "identity_type": "group",
  "identity_id": "group:developers"
}
```

Binding to a **group** is the default — it means "every member of this group inherits this policy." Group membership comes from ScaiKey and is resolved at request time, so adding a user to the group grants them access immediately.

Binding to a **user** or **service account** is direct. Useful for service accounts with their own explicit policies, less so for humans.

One policy can have many bindings. One identity can be covered by many policies. Effective permissions are the union.

## Evaluation order

A request arrives. ScaiVault:

1. Resolves the caller's direct bindings and group memberships.
2. Collects every policy matching those identities.
3. For each policy, iterates rules in order.
4. For each rule, checks pattern match and conditions.
5. If any rule in any policy permits the action on the path, the request is allowed.
6. Otherwise, `403 access_denied`.

There is no deny rule. Policies are additive. If you want to exclude a subset of a broader grant, use a narrower path pattern — don't try to write "allow `foo/**` except `foo/secret`."

## Testing access

Before you deploy a policy change, test it:

```bash
curl -X POST https://scaivault.scailabs.ai/v1/policies/test \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "identity_id": "user:alice@acme.example",
    "path": "environments/production/salesforce/api-credentials",
    "permission": "read",
    "context": {"source_ip": "10.0.1.50"}
  }'
```

Response tells you `allowed: true/false` and which policy and rule matched. Use this in CI to gate policy changes — write a test that asserts "the reporting service must be able to read the Salesforce secret" and fail the build if the assertion breaks.

## Partner- vs tenant-scoped policies

Policies live at a scope:

- **Tenant-scoped** policies affect paths within that tenant only.
- **Partner-scoped** policies affect `/v1/partner/secrets/*` paths (visible to every tenant under the partner).

Create by adding `"scope": "partner"` to the POST body (requires `partner_admin`).

## Default policies

A freshly-created tenant has one policy bound to the `tenant_admin` group: `{"path_pattern": "**", "permissions": ["admin"]}`. Every other access must be granted explicitly by an admin.

There is no "everyone can read everything" default.

## What's next

- [Policies API Guide](../api-guides/policies) — end-to-end policy management.
- [Multi-tenancy](./multi-tenancy) — identity and tenant scoping.
- [Policies Reference](../reference/policies) — endpoint details.
