06 EntailmentEN
06 · Entailment — the authorization binding
Normative source:
capability-language-core-v1.md§6.1 (Entailment), §6.3 (Algorithm), §6.4 (Match) This page is a readable guide. In case of conflict, the normative specification wins. Status: Preview — not for production use.
TL;DR
Binding answers "does this grant authorize this request?" on the authorization side (entailment) and "does this evidence correspond to this action?" on the evidence side (Match). Entailment decides class coverage: grant G covers operation O if the identifier matches and every declared parameter/constraint holds. Match decides content binding: an ActionId equals the recomputed projection — with no occurrence claims and no native verification.
§6.1 — Entailment, the two identifier rules
Grant G covers operation O when one of two things is true:
- Literal: G = O, byte-for-byte after normalization.
- Trailing wildcard: G =
scheme:prefix:*and O has at least one trailing segment afterscheme:prefix:— segment-boundary comparison, not lexical prefix.
| Grant | Operation | Result |
|---|---|---|
std/database-v1:query:* |
std/database-v1:query:SELECT |
✅ wildcard matches (entail-002) |
std/database-v1:query:* |
std/database-v1:query:SELECT:deep |
✅ multi-segment (entail-003) |
std/database-v1:query:* |
std/database-v1:admin:DDL |
❌ different namespace (entail-004) |
std/database-v1:query:SELECT |
std/database-v1:query:INSERT |
❌ literal mismatch (entail-006) |
Two shapes are always refused (§6.3 steps 1–2): a namespace mismatch (different_namespace, layer 3) and a wildcard with no trailing segment (entail-005, wildcard_requires_trailing_segment). The class-position wildcard (std/database-v1:*) is a grammar defect, not a superset — see 03-identifiers.md for the different_namespace pair entail-007/-008.
§6.3 — The Entails algorithm
Entails(G, O) → bool:
1. G.namespace ≠ O.namespace → false (namespace = scheme + action Class, §9.1 layer 3)
2. G.id doesn't cover O.id → false (path coverage, §9.1 layer 4)
3. G declares no key → true (no `params` and no `param_bounds` ⇒ unconstrained, absent ≡ empty object)
4. O.params absent → false (bounded grant, request omits it → `params_missing`)
5. params_subset(O.params, keys(params) ∪ keys(param_bounds), param_bounds)
(declared key set, §9.1 layers 5–9)
Working through the rulebook:
- Step 3 — unconstrained means all-params-welcome. A grant with no
paramsand noparam_bounds(or with"params":{}) covers any operation params. Absent and{}are semantically identical (§6.2; §7 rule 6; §9.1). - Step 4 — bounded grants are fail-closed on omission. If the grant narrows anything and the operation omits the whole
paramsfield,deny("params_missing")— one bound key missing reads the same as the whole object missing (decide-007); the case of a bounded grant against an operation carrying undeclared keys is pinned byundeclared-002, resolvedparams_missingbecause the missing-key check beats the undeclared-key check within layer 7. - Step 5 — the declared key set. Comparison runs over
keys(params) ∪ keys(param_bounds); aparam_boundskey additionally enforces its Bound (inclusivemin/max,step, enum cardinality,optional,nested— §6.5). Step 5 reduces to theparams-only comparison when noparam_boundsexist.
When more than one check fails, the reported reason follows the fixed §9.1 ordering, not the textual step order. The two orderings that surprise:
params_missingwins overundeclared_param(§9.1 layer 7 internal order), asundeclared-002shows.- Layer 6 (
invalid_params_null) resolves before presence. Anullparam value fails withinvalid_params_nulleven when the operation omits the wholeparamsfield — the null check shadows step 4'sparams_missing(decide-008→invalid_params_null).
Scheme defaults. A capability scheme may declare param_defaults; per §6.5 the implementation MUST materialize them into O before step 4, with precedence explicit operation value > scheme default > absent. Apply a default only when it is needed to satisfy a grant-declared key; optional:true keys are never materialized (the marker wins over the default).
§6.4 — Match (evidence binding)
Evidence E is bound to action A when:
- E carries a valid ActionId in the language's projection form
clc-action:1:…(§4.3); - E's ActionId equals the recomputed ActionId of the ObservedAction;
- the ActionId was computed under the relying-party-pinned suite and definition source.
Match is content correlation only (see 04-actions.md): it does not validate a native artifact, does not authorize execution, and does not identify an occurrence. Cross-format mapping (E's native format ≠ A's canonical form) must be pinned as an Action-Mapping Profile, with results EQUIVALENT_UNDER_PROFILE, NOT_EQUIVALENT, or INDETERMINATE.
Correlation quick-ref
| Binding | Side | Checks | Corpus examples |
|---|---|---|---|
| Entailment | authorization | class coverage: identifier + params + constraints | entail-001…008, params-001/002, decide-007/008 |
| Match | evidence | content identity: recomputed projection digest | evidence corpus (evidence-vectors.json) |
Common pitfalls
- Reading layer ordering off the algorithm text. §6.3's numbered steps are presentation; the authoritative outcome order is §9.1 (layers 1–9). When two checks fail, always prefer the fixed layer order over "the first step that failed."
- Mistaking the declared key set for an ordered list. Step 5 compares over the set
keys(params) ∪ keys(param_bounds); declaration order and field choice carry no semantic meaning for these checks. - Treating
allow_unresolvedas entailment's end. Entailment may succeed while the decision isallow_unresolvedbecause a constraint wasn't evaluated — that is §8.4's business, not entailment's.
← 05-grants.md · → 07-parameters.md · related: 10-decisions.md, 11-reason-codes.md