Access model
Understand the shared core of the model — attributes, targets, policies, the formula language, and the permit-only semantics every decision follows.
The access model is the artifact Arkveil evaluates. Everything that determines who may do what lives in it, as data. This page is the shared vocabulary for the rest of the documentation: the pieces every policy uses, and the semantics every decision follows. What is specific to protecting actions or protecting data lives on the Action policies and Data policies pages.
What the model consists of
- Attributes are properties of everything involved in a decision: the user, the action, the request, the data row, the context.
- Actions are the protected endpoints and functions of your application, named by
service:namecodes and optionally tagged. - Datasets are the protected tables, named by
datasource.schema.tablecodes. - Targets decide where policies apply. A target scopes either actions or datasets.
- Policies are the grants. Each policy attaches to one target and carries a condition.
- Tests are executable specifications of intended behavior.
Actions and datasets form the authorization surface. Targets select parts of it. Policies grant access within their target's scope. Tests pin the outcomes while the model evolves.
Attributes
Conditions reason over five namespaces:
| Namespace | Meaning | Schema lives |
|---|---|---|
user.* | who is making the request | workspace-wide — arkveil schemas get user |
action.* | the action under decision, including action.tags | workspace-wide — arkveil schemas get action |
context.* | runtime circumstances | workspace-wide — arkveil schemas get context |
request.* | parameters of this particular call | per action — its requestSchema |
data.* | columns of the protected table | per dataset — its dataSchema |
Attribute paths nest with dots, like user.profile.age. The exception is data.<column>, which names a single column and is available only where a dataset row is in scope — in a data-policy filter, and in conditions that look up dataset rows, like the exists lookup described below. request.* is available to action policies, following the schema declared on the action.
Schemas are enforced whenever a formula is created or edited. A condition that references an unknown attribute or a namespace not allowed in its context fails at parse time, not at runtime.
Roles are not special. user.role is an ordinary attribute, and access can turn on department, region, clearance, or anything else the schemas define.
Targets
A policy never applies globally. It attaches to a target, and the target decides the scope. Targets come in two types — ACTION and DATA — and three modes:
- INDIVIDUAL — exactly one action or dataset.
- CUSTOM — a group selected by a condition. For actions this usually means tags:
any action.tags where it = "payments"covers every action taggedpayments, and the group updates as actions are tagged and untagged. - ALL — everything of the target's type.
How each kind selects its scope in detail — action codes and tags on one side, dataset codes on the other — belongs to Action policies and Data policies.
Policies
A policy is a grant attached to one target.
- Type matches the concern: PERMISSION policies grant actions, while READ, TOUCH and RESULT policies govern data — READ for the rows a user may see, TOUCH for the existing rows a mutation may touch, RESULT for the row states it may produce.
- Status gates participation: a policy takes part in decisions while it is ENABLED. DRAFT and DISABLED policies are inert.
- Condition is a formula. In a permission policy, a true condition grants the action. In a data policy, the condition decides whether the policy contributes its filter — the second formula slot, naming the rows it opens. A policy without a condition is inactive.
Policy semantics are permit-only. There is no DENY effect and no combination algorithm: access is granted — or a granting filter applied — when at least one applicable policy's condition is true, and denied otherwise. Adding a policy can only widen access, and disabling one can only narrow it.
The formula language
A condition is one boolean expression, stored in the model and evaluated against the request's attributes. A data policy's filter is the same language — a second boolean expression, evaluated per row against data.*.
Comparisons and predicates. Equality is a single =, alongside !=, >, >=, <, <=. String predicates: contains, startsWith, matches (regular expression). Presence and set checks: is null, is not null, in [...], not in [...], is empty, is not empty.
Literals. Strings are double-quoted. Decimals need digits on both sides of the dot. Arrays are non-empty, single-typed, and hold literals only.
Boolean logic. and, or, not, with parentheses for grouping. Precedence from lowest to highest: or, and, not.
Iteration. Array attributes are tested element by element, with it naming the current element:
any user.permissions where it startsWith "billing:"
none action.tags where it = "blocked"
all request.items as line where line.it != ""Dataset references. A PERMISSION condition may ask whether a matching row exists, looked up at decision time:
exists demo_billing.public.invoice where (data.id = request.invoiceId and data.owner_id = user.id)After the where, data.<column> reads the referenced dataset's columns. A bare table name (exists invoice where …) is the usual spelling — it binds when the policy is saved and must match exactly one dataset. The full datasource.schema.table code names the same dataset explicitly, for when several share a table name. Evaluating a dataset reference requires a connected runtime. Asked of Arkveil Cloud alone, such a condition does not grant — the decision stays denied, fail-safe.
exists is the text form of a wider family. The engine also defines FETCH_ONE and FETCH references, which pull a matching row's values into a condition instead of only checking presence. Neither can be authored today — text syntax for them is planned, with the same reference rules.
Validate any formula before using it with arkveil formula parse. The complete formal reference — every operator, precedence, three-valued evaluation, and where each construct is allowed — is the Formula language page, shipped offline as arkveil formula syntax.
Evaluation
A decision starts with a request: user, context, and request attributes plus the action or the data operation. From there:
- Targets select the candidate policies — every ENABLED policy whose target covers the request.
- Each candidate's condition evaluates with the request's attribute values substituted in.
- For an action, the decision is granted when at least one condition is true, and denied otherwise. For a data operation, every policy of the relevant type whose condition holds contributes its filter — a row passes when any contributed filter is true for it, and no contributing policy means no rows.
Two properties make the semantics predictable:
- Null collapses to deny. A missing attribute or a type-mismatched comparison evaluates to null, and null propagates so the policy does not grant. A policy can fail safe. It cannot fail open.
- Changes are monotonic. Adding a policy can only widen access, and removing or disabling one can only narrow it. Negative tests pin the boundaries that must stay closed while the model grows.
Every decision is explainable. arkveil eval explain -a <code> --user '<json>' names the granting policies and the candidates. --json returns the full per-node evaluation trace, in the form coding agents consume. Arkveil Studio shows the same traces visually, for humans.
One model, two responsibilities
Action policies decide whether an action may happen. Data policies decide which rows it may see, change or create. The two are evaluated independently and enforced together, so a policy permitting an action never restates the rules that constrain its data. Everything on this page — attributes, targets, conditions, permit-only evaluation — is common to both.
Next steps
-
Action policies — this anatomy projected onto actions.
-
Getting started — the model in action, from seed to a protected endpoint.
-
CLI — the commands this page references.
-
Policy tests — the executable specifications that pin these semantics.