Start writing policies
A SASY policy answers a question about a proposed action: may this authenticated
caller perform it, given the recorded context? Policies use Soufflé Datalog in
.dl files. The engine supplies the common types and relations; your file adds
rules.
A small policy
Section titled “A small policy”Save this as read-only.dl:
// Allow Read calls. Other tool names have no allowlist match.IsAuthorized(idx) :- Actions(idx, action), IsTool(action, "Read").
// @deny_message: Writes are disabled in this session// @suggestion: Read the existing file and describe the proposed changeUnauthorized(idx) :- Actions(idx, action), IsTool(action, "Write").The first rule allows a Read call. The second explicitly blocks Write and
provides feedback. An action with no allowlist match is not authorized; a hard
denial wins even if an allow rule also matches.
Bind the file to a session with sasy.session(policy=Path("read-only.dl")), as in
use the SDK directly. The engine compiles it when the session
binds. The application checks the actual proposed action before executing it.
This policy checks tool names; it does not restrict which files Read may access.
Facts and rules
Section titled “Facts and rules”A relation is a table of facts. Actions contains the proposed actions,
Principal identifies the authenticated caller, and Edge records message
dependencies. The engine supplies those facts; your rules derive new ones.
Read IsAuthorized(idx) :- Actions(idx, action), IsTool(action, "Read"). as:
“put idx on the allowlist if that action is a Read tool call.”
- Commas mean AND. Separate rules with the same head mean OR.
- Variables join matching values across relations.
_matches any value without retaining a binding; quoted strings are constants. - Rules end with a period.
//starts a comment. - Declare a relation you introduce with
.decl; common relations are already declared by the engine.
A check can propose several actions. idx is their zero-based index, shared by
allow and denial results. A tool action is a tagged value, such as this input fact:
Actions(0, $CallTool("Read", "{\"file_path\":\"notes.txt\"}")).The application supplies this fact; do not hard-code it in a policy.
$CallTool contains the tool name and a JSON argument string.
Read an argument
Section titled “Read an argument”An allow rule can constrain the action’s arguments:
IsAuthorized(idx) :- Actions(idx, action), action = $CallTool("publish", args), @json_get_str(args, "destination") = "internal".@json_get_str reads a string field. A missing field does not match "internal",
so this rule alone does not allow a publication with no destination. More rules
can inspect recorded messages, successful tool results, and authenticated roles.
Continue writing policies
Section titled “Continue writing policies”| Page | What it explains |
|---|---|
| Core relations | Actions, authorization, identity, message fields, and dependency direction. |
| Built-ins and feedback | More relations, JSON and text functors, denial reasons, and approval notation. |
| Extension points | Metadata scopes and producer APIs, policy-specific relations, and custom functors. |
| Cookbook | Rules for tool arguments, information flow, approvals, roles, and unknown-origin inputs. |
| Validation and debugging | Compilation, behavioral checks, denial traces, and static analysis. |
| Dependency relevance | The IDB relevance gates, automatic inference, and safe ways to narrow traversal. |
To check a file before binding it, see validation. For observations supplied by a specific framework, see the integration guides and the LangChain or ADK record references.