Skip to content

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.

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 change
Unauthorized(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.

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.

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.

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.