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#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
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:
1 2 3 4 5 6 7 8 | |
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:
1 2 3 4 5 6 | |
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:
- Resolves the caller's direct bindings and group memberships.
- Collects every policy matching those identities.
- For each policy, iterates rules in order.
- For each rule, checks pattern match and conditions.
- If any rule in any policy permits the action on the path, the request is allowed.
- 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:
1 2 3 4 5 6 7 8 9 | |
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 — end-to-end policy management.
- Multi-tenancy — identity and tenant scoping.
- Policies Reference — endpoint details.