Concepts
Runtime EDB
Supply a complete snapshot of request facts to one bounded policy evaluation.The EDB (Extensional Database) contains the input facts supplied by the
application for one evaluation. For example, owns("alice", "roadmap.pdf")
says who owns a document. A fact is ground when it contains concrete values,
not variables.
The solver reads this input and derives new facts in the result, not in the EDB. The loaded ruleset supplies the rules; the domain supplies the allowed predicate names, arities and roles. The public input buffer is independent of a policy: its contents are checked against the selected policy when solving.
For programming details, see C Stable EDB, Python EDB or TypeScript EDB.
Why EDB exists
EDB separates what changes for each request from the rules reused across requests.
| Concern | Example | Lifetime |
|---|---|---|
| Runtime input | Ownership, sharing and a user's sensitivity level. | Facts supplied for this evaluation. |
| Reusable logic | An owner may read a document if the required conditions hold. | Loaded once; evaluated for many requests. |
| Input responsibility | The application supplies the evidence the policy needs. | Checked before each authorization decision. |
For the document_access example, the EDB might contain:
owns("alice", "roadmap.pdf").
shared_with("bob", "roadmap.pdf").
sensitivity_level("alice", 4).
sensitivity_level("bob", 3).
Those facts change per request. The ruleset does not.
Where EDB fits
The EDB sits between caller input and solver output.
Applicationobserves the request
Input EDBstores concrete facts
Solveapplies the prepared policy
Resultinput snapshot + conclusions
- Application → Input EDB
- Input EDB → Solve
- Solve → Result
EDB vs IDB
| Type | Source | Example |
|---|---|---|
| EDB | Application supplies facts at runtime. | owns("alice", "roadmap.pdf") |
| IDB | Solver derives facts by applying rules. | allow("alice", "roadmap.pdf") |
| POLICY_FACT | Policy author writes facts directly in the source. | blocked("mallory"). when blocked/1 has that role. |
EDB facts are what the application reports. IDB facts are what the policy concludes. A predicate has exactly one of these three origins.
Lifecycle
Create the input buffer once, then build the complete facts for a request.
Create bufferchoose bounded capacities
Add factsone fact or an atomic batch
Solvevalidate, sort and deduplicate
Inspect resultquery and optional explanations
Release resultthe session can solve again
- Create buffer → Add facts
- Add facts → Solve
- Solve → Inspect result
- Inspect result → Release result
There is no separate EDB finalization step in the current public API. Solving prepares the engine's input snapshot internally.
For another request:
- Release the previous result before solving again on the same session.
- Clear or reset the input buffer, or create another buffer.
- Supply the new request's complete facts, then solve with the same session.
Clearing an input buffer does not release a result. Conversely, closing or
changing the input buffer does not change a returned result's snapshot.
C permits reuse of the buffer directly; Python seals a successfully solved EDB
and provides reset() to start a new batch. The programming pages describe
each binding's exact lifetime rules.
Adding facts
Two vocabulary modes
There are two places where the same string can appear, with different checks.
| Where the string appears | Example | Must be declared in domain atoms? |
|---|---|---|
| Policy source | not(blocked("mallory")) | Yes, by default. It is a constant chosen by the policy author. |
| Runtime EDB | blocked("mallory") supplied for a request | No. It is a request value supplied by the application. |
The short rule:
Constants in
.dlare policy vocabulary. Facts in the EDB are runtime evidence.
Declaring "mallory" in atoms does not insert a fact. It permits that string
to be written in policy source. Inline loading always requires declared source
strings; a manifest loader has an explicit permission to accept undeclared
policy-source atoms. That permission does not allow undeclared predicates.
Strings that belong to the policy program (operation names, deny codes,
protected branch names) are written in the .dl source. Strings observed at
request time (user names, paths, messages) belong in the EDB. Both are matched
by their string value during evaluation.
What is a fact?
A fact has a predicate name and a fixed number of typed terms.
owns("alice", "roadmap.pdf").
age("bob", 34).
active(true).
owns/2has two terms: a user and a document, both strings.age/2has two terms: a string and an integer.active/1has one term: a boolean.
The predicate must be declared EDB in the selected domain. A
POLICY_FACT predicate belongs to policy source; an IDB predicate belongs
to rule conclusions. Neither can be injected as runtime input.
Every predicate has exactly one arity. If owns/2 is declared, supplying
owns("alice") is a shape error, not a different relation. See
Predicate identity.
Input insertion copies predicate names and string values into the buffer. A batch is atomic: if one entry cannot be added, none of that batch is added. Shape and storage checks happen on insertion; checks against the selected domain, including the declared arity and EDB role, happen when solving.
The three term types
| Type | Use when | Example |
|---|---|---|
| Symbol | A name, path or string identifier. | "alice", "roadmap.pdf" |
| Integer | A whole number. | 42, -7, 1024 |
| Boolean | A true/false value. | true, false |
Runtime integers are signed 64-bit values. A variable such as User is a
placeholder in a rule, not a fourth kind of value accepted in an EDB fact.
Integer inputs can be tested by arithmetic filters such as
Score + 1 >= 10. The filter tests already-bound values; it does not create
new ones. Numeric aggregates have a narrower supported value range; see
the language guide.
Why symbols are different from ints and bools
Internally, the engine interns strings: it associates each distinct string with a compact identifier so later comparisons can use identifiers instead of comparing all the bytes again.
| String | Internal identity in one evaluation |
|---|---|
"alice" | One identifier. |
"bob" | A different identifier. |
"alice" again | The same identifier as before. |
The public input API accepts strings, not identifiers you must intern yourself. The engine reconciles policy constants and runtime strings when preparing the evaluation.
EDB constraints
| Constraint | What is bounded |
|---|---|
| Stored entries | The number of added facts, before deduplication. |
| Text storage | Copied predicate names and string terms. |
| Arity | The number of terms accepted in a fact. |
| Facts per predicate | The selected profile's relation capacity, checked during evaluation. |
These bounds are not all checked at the same moment. An insertion can fail because the buffer is full; solving can fail because facts do not match the domain or exceed a relation capacity. Neither failure publishes a partial result. For numeric capacities, status names and diagnostics, see C Stable errors.
What you have learned
KEEP THIS MODEL
Build a complete input snapshot- Request facts are concrete inputs
Each fact gives a predicate name and its typed values, not rule variables. Supply all facts needed to evaluate the current request.
- The application chooses trustworthy evidence
The engine evaluates the supplied facts; it does not authenticate them or invent missing evidence.
- Copied inputs and results have separate lifetimes
The input buffer copies its strings. Clearing or closing it does not change a result already returned by a successful solve.
- A batch is inserted together
If batch insertion fails, none of its new facts are added. Domain and relation-capacity checks also occur during solving.
- Limits fail explicitly
Insertion can fail when input storage is full; solving can fail when facts violate the domain or relation limits. Neither failure publishes a partial result.