Concepts
Solving
Evaluate a ruleset and runtime facts into a bounded set of derived conclusions.Solving evaluates the ruleset — what the policy says — against the EDB — facts supplied for this request. It derives the conclusions that follow from the rules.
A session prepares a selected policy for repeated evaluations and holds its working memory. Each successful solve returns a result snapshot that can be queried. A session permits only one live result at a time. Release that result before solving another request on the same session.
For programming details, see C Stable solving, Python solving and TypeScript solving.
Why solving exists
A loaded policy does not yet give an answer. The EDB does not give an answer either: it contains evidence, not the consequences of that evidence.
| Ingredient | Meaning |
|---|---|
| Ruleset | The parsed policy facts and rules. |
| EDB | Ownership, roles, context and other request facts. |
| Solve | Apply those rules to those facts until no new conclusion follows. |
The ruleset is parsed once and reused. The session keeps the preparation. Only the request input needs to change between evaluations.
Where solving fits
Prepared policy + EDBrules and complete request facts
Solveevaluate them together
Resultsnapshot to inspect
- Prepared policy + EDB → Solve
- Solve → Result
See Runtime EDB for building input and Querying for reading the result.
Solve lifecycle
Prepare sessiononce for this policy
Supply factscomplete request snapshot
Solveno separate finalize call
Inspect resultmembership + explanations
Release resultthen start the next request
- Prepare session → Supply facts
- Supply facts → Solve
- Solve → Inspect result
- Inspect result → Release result
For a second request, retain the prepared session, replace the input facts, and solve again after releasing the first result. No session-reset call or policy reload is needed: the engine prepares the next request's working state when that solve starts.
The result is read-only. Clearing or closing the caller's input buffer does not change it. A manually prepared explanation must also be released before the result it uses can be released.
Evaluation model
Maelys DL uses semi-naive positive Datalog evaluation, with ordered evaluation layers for negation and aggregates.
- Start with policy facts and request facts.
- Apply rules to derive new facts.
- In subsequent passes, use newly derived facts — the delta — to find further consequences.
- Stop when no new facts can be derived. This is the least fixpoint: exactly the consequences justified by the program and its input.
The number of passes depends on the dependency structure and the data. Even a non-recursive chain can require propagation through several rules; there is no general promise of exactly two passes.
Stratified negation
A stratum is an evaluation layer. A relation used through not(...)
is completed before the higher layer tests its absence. A dependency cycle
through negation is rejected when loading.
/* Base evidence: blocked/1 is POLICY_FACT */
blocked("mallory").
/* A later layer may test absence of that evidence */
allow(User, Doc) :-
owns(User, Doc),
not(blocked(User)).
Here the domain must declare "mallory" as a source atom.
blocked("mallory") is a policy fact in this example; another policy can
instead declare blocked/1 as EDB and receive it from the application.
Aggregates use the same completion principle: count, min, max
and sum read a completed lower relation. Positive recursion can finish
in that lower layer; a cycle through the aggregate is rejected. See
Rulesets and
the language guide.
Fail-closed behavior
If solving fails, no partial result is returned. The application receives an error and can inspect the diagnostic where the interface provides one.
Examples include invalid input predicates, type mismatches, capacity exhaustion and backend failures. These are errors, not successful negative queries.
A successfully solved result may later produce a truncated explanation. That limits what can be displayed, not the already-computed decision. Event windows add a publication boundary: a failed proposed update leaves the previous committed snapshot available.
Session and policy lifetime
The engine manages the compiled policy's lifetime for existing sessions: releasing the loading handle does not invalidate sessions already created from it. In the reference engine, engine-owned sessions share immutable compiled policy storage, while request state remains separate for each session. This reduces setup memory; it is not a claim that every evaluation becomes faster.
What you have learned
KEEP THIS MODEL
Prepare once, solve each request separately- Prepare reusable logic
Load the policy and prepare a session once.
- Start with the whole request
Supply a complete input snapshot for each request.
- Query only a successful result
Solve, then query the successful result. A failed solve is an error, not a negative membership answer.
- Release before reusing the session
Release explanations and the result before the next solve on that session.
- Keep the session for later requests
Keep the session until no more requests need it.