Regolix (regolix v0.3.1)

Copy Markdown View Source

Elixir wrapper for the Regorus Rego policy engine.

Basic Usage

{:ok, engine} = Regolix.new()
{:ok, engine} = Regolix.add_policy(engine, "authz.rego", """
  package authz
  default allow = false
  allow if input.user == "admin"
""")
{:ok, engine} = Regolix.set_input(engine, %{"user" => "admin"})
{:ok, true} = Regolix.eval_query(engine, "data.authz.allow")

Coverage Tracking

Track which policy lines are executed during evaluation:

{result, coverage} = Regolix.with_coverage(engine, fn e ->
  Regolix.eval_query!(e, "data.authz.allow")
end)
# coverage => %{"authz.rego" => %{covered: [1, 2, 5], not_covered: [9, 10]}}

For multi-query accumulation:

engine = Regolix.enable_coverage!(engine)
Regolix.eval_query!(engine, "data.authz.allow")
Regolix.eval_query!(engine, "data.rbac.check")
coverage = Regolix.get_coverage_report!(engine)
engine = Regolix.disable_coverage!(engine)

Summary

Functions

Adds data to the engine's data document.

Adds data to the engine. Raises on error.

Adds a Rego policy to the engine.

Adds a Rego policy to the engine. Raises on error.

Clears accumulated coverage data without disabling coverage.

Clears all data from the engine, keeping policies intact.

Clears all data from the engine. Raises on error.

Disables coverage tracking on the engine.

Enables coverage tracking on the engine.

Evaluates a Rego query against the engine.

Evaluates a Rego query. Raises on error.

Returns the accumulated coverage report.

Returns the accumulated coverage report. Raises on error.

Returns the list of package names loaded in the engine.

Returns metadata about rules defined in loaded policies.

Returns metadata about rules defined in loaded policies. Raises on error.

Creates a new Rego policy engine.

Creates a new Rego policy engine. Raises on error.

Sets the input document for policy evaluation.

Sets the input document. Raises on error.

Executes a function with coverage tracking enabled, returning both the result and coverage.

Types

coverage_report()

@type coverage_report() :: %{
  required(String.t()) => %{
    covered: [pos_integer()],
    not_covered: [pos_integer()]
  }
}

engine()

@type engine() :: reference()

eval_result()

@type eval_result() :: json_encodable() | :undefined

json_encodable()

@type json_encodable() :: map() | list() | String.t() | number() | boolean() | nil

rule_info()

@type rule_info() :: %{
  name: String.t(),
  description: String.t(),
  start_line: pos_integer(),
  end_line: pos_integer()
}

Functions

add_data(engine, data)

@spec add_data(engine(), json_encodable()) ::
  {:ok, engine()} | {:error, Regolix.Error.t()}

Adds data to the engine's data document.

Can be called multiple times to merge data.

Examples

{:ok, engine} = Regolix.add_data(engine, %{"users" => %{"alice" => %{"role" => "admin"}}})

add_data!(engine, data)

@spec add_data!(engine(), json_encodable()) :: engine()

Adds data to the engine. Raises on error.

add_policy(engine, name, source)

@spec add_policy(engine(), String.t(), String.t()) ::
  {:ok, engine()} | {:error, Regolix.Error.t()}

Adds a Rego policy to the engine.

Examples

{:ok, engine} = Regolix.add_policy(engine, "authz.rego", "package authz")

add_policy!(engine, name, source)

@spec add_policy!(engine(), String.t(), String.t()) :: engine()

Adds a Rego policy to the engine. Raises on error.

clear_coverage!(engine)

@spec clear_coverage!(engine()) :: engine()

Clears accumulated coverage data without disabling coverage.

Use this to reset coverage between test runs while keeping coverage enabled.

Examples

engine = Regolix.clear_coverage!(engine)

clear_data(engine)

@spec clear_data(engine()) :: {:ok, engine()} | {:error, Regolix.Error.t()}

Clears all data from the engine, keeping policies intact.

Examples

{:ok, engine} = Regolix.clear_data(engine)

clear_data!(engine)

@spec clear_data!(engine()) :: engine()

Clears all data from the engine. Raises on error.

disable_coverage!(engine)

@spec disable_coverage!(engine()) :: engine()

Disables coverage tracking on the engine.

Coverage data is retained until explicitly cleared with clear_coverage!/1.

Examples

engine = Regolix.disable_coverage!(engine)

enable_coverage!(engine)

@spec enable_coverage!(engine()) :: engine()

Enables coverage tracking on the engine.

After enabling, all query evaluations will track which policy lines are executed. Use get_coverage_report/1 to retrieve the accumulated coverage data.

Examples

engine = Regolix.enable_coverage!(engine)

eval_query(engine, query)

@spec eval_query(engine(), String.t()) ::
  {:ok, eval_result()} | {:error, Regolix.Error.t()}

Evaluates a Rego query against the engine.

Returns the result as Elixir terms, or :undefined if the query has no result.

Examples

{:ok, true} = Regolix.eval_query(engine, "data.authz.allow")
{:ok, :undefined} = Regolix.eval_query(engine, "data.authz.nonexistent")

eval_query!(engine, query)

@spec eval_query!(engine(), String.t()) :: eval_result()

Evaluates a Rego query. Raises on error.

get_coverage_report(engine)

@spec get_coverage_report(engine()) ::
  {:ok, coverage_report()} | {:error, Regolix.Error.t()}

Returns the accumulated coverage report.

Returns coverage data for all policy files, showing which lines were executed (covered) and which were not (not_covered).

Examples

{:ok, coverage} = Regolix.get_coverage_report(engine)
# => %{"authz.rego" => %{covered: [1, 2, 5], not_covered: [9, 10]}}

get_coverage_report!(engine)

@spec get_coverage_report!(engine()) :: coverage_report()

Returns the accumulated coverage report. Raises on error.

get_packages(engine)

@spec get_packages(engine()) :: [String.t()]

Returns the list of package names loaded in the engine.

Examples

packages = Regolix.get_packages(engine)
# => ["data.authz", "data.rbac"]

get_rules(engine)

@spec get_rules(engine()) ::
  {:ok, %{required(String.t()) => [rule_info()]}} | {:error, Regolix.Error.t()}

Returns metadata about rules defined in loaded policies.

Parses the policy sources to extract rule names, descriptions (from comments), and line ranges. Useful for mapping coverage line numbers to human-readable rule names.

Examples

rules = Regolix.get_rules(engine)
# => %{
#   "policy.rego" => [
#     %{name: "allow", description: "Allow if not denied", start_line: 10, end_line: 15},
#     %{name: "deny", description: "Deny sanctioned countries", start_line: 20, end_line: 25}
#   ]
# }

get_rules!(engine)

@spec get_rules!(engine()) :: %{required(String.t()) => [rule_info()]}

Returns metadata about rules defined in loaded policies. Raises on error.

new()

@spec new() :: {:ok, engine()}

Creates a new Rego policy engine.

Examples

{:ok, engine} = Regolix.new()

new!()

@spec new!() :: engine()

Creates a new Rego policy engine. Raises on error.

set_input(engine, input)

@spec set_input(engine(), json_encodable()) ::
  {:ok, engine()} | {:error, Regolix.Error.t()}

Sets the input document for policy evaluation.

Accepts Elixir terms (maps, lists, etc.) which are automatically JSON-encoded.

Examples

{:ok, engine} = Regolix.set_input(engine, %{"user" => "alice"})

set_input!(engine, input)

@spec set_input!(engine(), json_encodable()) :: engine()

Sets the input document. Raises on error.

with_coverage(engine, fun)

@spec with_coverage(engine(), (engine() -> result)) :: {result, coverage_report()}
when result: var

Executes a function with coverage tracking enabled, returning both the result and coverage.

This is the recommended API for single-operation coverage. Coverage is automatically enabled before the function runs and disabled/cleared afterward.

Examples

{result, coverage} = Regolix.with_coverage(engine, fn e ->
  Regolix.eval_query!(e, "data.authz.allow")
end)
# result => true
# coverage => %{"authz.rego" => %{covered: [1, 2, 5], not_covered: [9, 10]}}

For multi-query accumulation, use the raw primitives: