Conditions
Relation conditions in OpenFGA and call conditions in CEL in the PDP, and why each lives where it does.
There are two kinds of condition, and the criterion to split them is simple: what the condition is about.
Relation condition
It is a property of the relation, known when the relation is published. Today there is one: expiration. A delegation or a capability can have an end date. OpenFGA evaluates the condition with the current time that the PDP sends, as described in the OpenFGA guide on conditions. When it expires, the relation stops existing for the decision, without anyone having to delete it.
Call condition
It is a CEL expression that reads the call data: the original arguments, the subject, the object of the call and the trusted attributes that the PIP brought from a context source. In the Admin it is a rule, written per edge, the agent · server · tool combination. A rule like this one, on a refund tool, limits the amount to the refund limit that the HR API returned for the person who delegated:
context.chokepoint.arguments["amount"] <= subject.properties.chokepoint["<binding>"].values["refund_limit"]<binding> is the identifier of the binding that supplies the trusted attributes; the rule editor fills it in.
The condition must return a boolean. It declares the bindings it depends on, and if one is missing, the call is blocked instead of evaluated without the trusted attribute. Numbers reach CEL as double, so integer operations need an explicit int().
Presence and binding
A call condition can also read how the token was obtained (Trusted context). This condition, on a tool that deletes data, allows the call only from a harness login, with a person present:
context.chokepoint.subject.presence == "interactive"And this one also requires a token bound to the key of the harness with DPoP:
context.chokepoint.subject.presence == "interactive" && context.chokepoint.binding == "dpop"Nothing is denied by presence by default: without a condition, both modes call the same tools.
Where the limit comes from
The condition compares a call value with a limit. The limit comes from one of these places:
| Source | Example | Applies to |
|---|---|---|
| A value fixed in the Admin rule | amount <= 1000 | Every call of the agent on that tool |
| A fact from the customer's trusted API (PIP) | the refund limit that billing returned | The subject or the resource looked up |
| A limit of the agent instance | max_amount = 100 in the token | Only the calls of that instance |
Instances of the same agent run at the same time, with the same relations. The limit of an instance comes in the token it receives in the exchange, in the authorization_details parameter of RFC 9396. The token is signed, so a changed limit invalidates the signature (Instance limits has the format and the validation rules). The rule reads the limit in context.chokepoint.limits:
double(context.chokepoint.arguments.amount) <= double(context.chokepoint.limits.max_amount)The Admin does not offer instance limits for now: the agent instance sets its own limit in its token, so it is not a control of the administrator; a limit is checked on each call, with no total across calls; and harness login sends no limits.
The instance limit only narrows access. All conditions of the edge apply together, so the fixed Admin value still applies. If the rule declares a limit and the token has none, the call is denied with reason condition.
Why the condition sits on the edge
A condition tied to a person would have to be repeated for each person. Tied to a resource, it would apply to any agent that touched it. Tied to the agent · tool edge, it describes what that agent can do with that tool, which is the unit that the administrator configures and that the Admin shows.
A call count limit is not a relation condition in OpenFGA: it needs to count calls with a reservation before execution and rules for failures and time windows, and it is not part of the current model.