» Language: Imports

Imports enable a Sentinel policy to access reusable libraries and external data and functions. The exact list of available imports is dependent on the host system. Host systems may also make the available imports configurable via plugins.

Imports are extremely powerful. They enable policy decision based on arbitrary external information. For example, a behavior coming into a system can be allowed or denied based on information from a separate system where the two systems can be unaware of each other.

The example below shows a concrete example. For the example, imagine that the import calendar allows access to engineer calendars. This import only exists for the purpose of this example and at the time of writing is not a real available import.

import "calendar"

// Get the calendar for Bob for today
bob_calendar = calendar.for("bob").today

// Allow this policy to pass if Bob is not on vacation.
main = rule { not bob_calendar.has_event("vacation") }

This example shows an import being used to deny a behavior if Bob is on vacation. Hopefully real policies do not depend on a single person being in the office, but the example shows the power of imports.

In addition to consuming imports, you can also extend Sentinel by writing custom imports This allows you to add any arbitrary behavior to your Sentinel-protected systems.

» Importing

Whether accessing a built-in library or an external plugin, the syntax for an import is the same:

import "name"

This adds the import with the name name. It can be referenced using the identifier name. For example, if the import above exposed first and last name data, you could access it via name.first or name.last.

You can shadow imports by assigning a variable. In the example below, the import name becomes unavailable within the function because it is shadowed by a variable of the same name.

Shadowing an import is not the same as overwriting a variable. Imports are not part of the scope, meaning that the shadow only exists for the scope it is created within. For everything else, the import works even after it is shadowed. The example below shows that as well.

import "name"

f = func() {
    name = "jane"
    return name
}

print(f())        // "jane"
print(name.first) // "bob"

» Aliases

An import can be aliased to another name using the syntax below:

import "name" as other

This would make the import "name" available as other.

An import may only be imported once, regardless of aliases. The following would be invalid and would result in an error:

import "name" as one
import "name" as two

» Accessing Import Data

Imports are accessed with dot-separated identifiers, such as name.first or name.stage.first. These are called selector expressions. An import may return nested data that further can be accessed via selector expressions.

In the first example on this page with the "calendar" import, we see this:

import "calendar"

a = calendar.for("bob") // selector expression calendar.for
b = a.today             // selector expression on the result
c = b.vacation          // accessing further nested data

The exact structure of an import is dependent on the import. Please reference the documentation for an import to learn more.