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.

IngredientMeaning
RulesetThe parsed policy facts and rules.
EDBOwnership, roles, context and other request facts.
SolveApply 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

ARCHITECTUREEvaluate a policy against a request
    • Prepared policy + EDBrules and complete request facts
    • Solveevaluate them together
    • Resultsnapshot to inspect

See Runtime EDB for building input and Querying for reading the result.

Solve lifecycle

ARCHITECTUREEvaluate, inspect, release, repeat
    • Prepare sessiononce for this policy
    • Supply factscomplete request snapshot
    • Solveno separate finalize call
    • Inspect resultmembership + explanations
    • Release resultthen start the next request

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.

DATALOG
/* 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.