Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "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
1
2
3
4
5
6
7
8
{
  "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
1
2
3
4
5
6
{
  "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
1
2
3
4
5
6
7
8
9
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#

Updated 2026-05-17 13:26:50 View source (.md) rev 2