oso 0.12.0

Core (All libraries)

Breaking Changes


This release contains breaking changes. Be sure to follow migration steps before upgrading.

Unifying with a predicate now fails at parse time

Previously it was possible to bind predicates to variables, e.g. p = f(x). This supported an undocumented feature for unifying predicates, e.g. f(x) = f(1) would unify x = 1.

To avoid confusion with querying rules, binding predicates to variables is no longer legal in this release. For example:

f(x,y) if x < 3 and y = g(x);

will now fail at parse time.

A similar thing can be achieved by using lists. Instead of f(x) = f(1), you can write ["f", x] = ["f", 1] if necessary.

Query for unbound variables

Querying for an unbound variable x is inferred to mean x = true. This is consistent with the behavior for a bound variable, and makes it possible to use this syntax with data filtering, i.e:

allow(_, _, resource) if

Before: f(x) if x; would error if x was unbound.

Now: this is interpreted as f(x) if x = true;.

Other bugs & improvements

Increased performance for data filtering

The performance of data filtering queries has been improved up to 3x with queries involving larger policies seeing the greatest speed ups. We continue to work on performance of data filtering queries and Polar. We’ve added more benchmarks to our test suite to cover data filtering queries. However, it’s helpful to have real world examples to base our performance work on. Join our slack to share your policies with us!


New features

get_allowed_actions() in Rust

Added support for get_allowed_actions(). You can use this method to get all actions that an actor is allowed to perform on a resource.

// get a HashSet of actions as strings
let actions: HashSet<String> = oso.get_allowed_actions(actor, resource)?;


Thanks to @joshrotenberg for adding this in PR #789.

Other bugs & improvements

  • The Rust CLI now uses clap to expose a prettier interface thanks to @joshrotenberg via PR #828.
  • Added FromPolar and ToPolar implementations for more std::collections types. Thanks to @gjvnq for PR #822!


Other bugs & improvements

  • Added free() method to enable manually freeing the underlying Polar WASM instance. This should not be something you need to do during the course of regular usage. It’s generally only useful for scenarios where large numbers of instances are spun up and not cleanly reaped by the GC, such as during a long-running test process in ‘watch’ mode.

  • The Polar Variable type is now exposed in the Node.js library, allowing users to pass unbound variables to queryRule() and isAllowed().

    const oso = new Oso();
    await oso.loadStr('hello("world"); hello("something else");');
    const query = oso.queryRule("hello", new Variable("var"));
    for await (const result of query) {
    => Map(1) { 'var' => 'world' }
    => Map(1) { 'var' => 'something else' }


Other bugs & improvements

  • Go lib no longer tries to print the zero values it uses for bookkeeping. This would crash when running on macOS under delve.

  • RegisterClass() and RegisterClassWithName() now accept an instance of the to-be-registered Go type instead of a reflect.Type instance:

    // Previously supported & still works:
    oso.RegisterClass(reflect.TypeOf(Expense{}), nil)
    // Now supported:
    oso.RegisterClass(Expense{}, nil)

    Thanks to @delicb for suggesting this in #816 and implementing it in #820!

Connect with us on Slack

If you have any questions, or just want to talk something through, jump into Slack. An Oso engineer or one of the thousands of developers in the growing community will be happy to help.