Policy
Allow internal publication. Allow external publication only when no confidential information influenced it.
These examples use real framework execution and SASY enforcement, with scripted models and simulated effects. No model-provider key is required. With the quick-start engine running, run the commands below from the repository root in another terminal.
An agent reads a document, passes it through a drafting tool, and asks a publication tool to send the rewritten draft to an internal or external destination.
Policy
Allow internal publication. Allow external publication only when no confidential information influenced it.
Datalog excerpt
.decl ConfidentialInput()ConfidentialInput() :- CurrentDepends(id), ToolResult(id, "read_confidential", _).
Unauthorized(idx) :- Actions(idx, a), IsTool(a, "publish"), a = $CallTool(_, args), @json_get_str(args, "destination") = "external", ConfidentialInput().CurrentDepends follows the inputs behind the publication request. The
full policy
also defines the allowed actions.
# Denied: confidential input, external destinationuv run examples/langchain-information-flow/demo.py# Allowed: keep confidential input internaluv run examples/langchain-information-flow/demo.py --destination internal# Allowed: publish a summary based only on public inputuv run examples/langchain-information-flow/demo.py --publicThe default run is denied before the publication handler runs, leaving
simulated_deliveries empty. The internal and public-input runs each make one
simulated delivery.
The LangChain guide explains the policy and includes a supervisor with parallel workers. See the example README for more scenarios and live-model execution.
A requester submits a payment, a reviewer approves it, and a payer tries to add it to an in-memory ledger. The agents pass their results through a serial ADK workflow.
Policy
A payment is only allowed when it exactly matches a prior request which has been approved by a reviewer.
Datalog excerpt
IsAuthorized(idx) :- Actions(idx, a), a = $CallTool("disburse_payment", args), ApprovedPayment( @json_get_str(args, "request_id"), @json_get_str(args, "payee"), @json_get_str(args, "amount")).ApprovedPayment joins successful request and approval tool results in
CurrentDepends, checks their agent labels, and matches all three fields.
See its definition in the
full policy.
# Allowed: the request, approval, and payment matchuv run examples/adk-separation-of-duties/main.py# Denied: the payer names a different payeeuv run examples/adk-separation-of-duties/main.py --scenario wrong-payeeThe default run records one payment under dispatched. The wrong-payee run is
denied and leaves the ledger empty.
See the example README for the policy and other scenarios, and the ADK guide for supported configurations.
The research and records agents read documents and pass summaries to main,
which can send simulated email but cannot read the documents itself. An inbox
document contains an injected instruction to forward a confidential merger plan
to an external auditor. Run the same team under two policies.
Policy
Readers must be cleared for the documents they open. Email recipients must be cleared for every document that influenced the send.
Datalog excerpt
Unauthorized(idx) :- Actions(idx, a), IsTool(a, "send_email"), a = $CallTool(_, args), to = @json_get_str(args, "to"), Clearance(to, clearance), ReadInDeps(path), FileLevel(path, level), level > clearance.Policy
Allow reads and internal sends. Deny an external send when both an untrusted and a confidential document influenced it. Confidential input alone does not trigger this denial.
Datalog excerpt
Unauthorized(idx) :- Actions(idx, a), IsTool(a, "send_email"), a = $CallTool(_, args), to = @json_get_str(args, "to"), !Internal(to), UntrustedInDeps(), ConfidentialInDeps().ReadInDeps finds document reads in CurrentDepends; UntrustedInDeps and
ConfidentialInDeps classify those reads. The full
MLS policy
and toxic-flow policy
define these helpers, document levels, clearances, and allowed actions.
# Enforce document and recipient clearancesuv run examples/langroid-information-flow/demo.py --policy mls# Block external sends combining untrusted and confidential inputuv run examples/langroid-information-flow/demo.py --policy toxic-flowBoth deny the injected request to send a confidential merger plan to an external auditor. A user-requested send of the plan to an uncleared partner is denied by MLS but allowed by toxic flow when no untrusted input influenced it.
See the example README for the policies and concurrent scenarios, and the Langroid guide for supported configurations.
Set the provider credentials in the repository-root .env: OPENAI_API_KEY
for LangChain and Langroid, or GOOGLE_API_KEY for ADK. Add --live to use a
real model; Langroid also requires --model:
# LangChain: use the live model instead of the scripted modeluv run examples/langchain-information-flow/demo.py --live# ADK: use Gemini for the same payment workflowuv run examples/adk-separation-of-duties/main.py --live# Langroid: choose an OpenAI model and enforce the MLS policyuv run examples/langroid-information-flow/demo.py --policy mls --live --model gpt-4.1-miniSASY still enforces the selected policy before tools execute. Live runs make paid provider requests; documents, payments, and deliveries remain simulated. The model may choose different actions, so inspect the tool decisions rather than expecting the scripted results.
These examples bind their policies per session. SASY can concurrently enforce different policies for multiple agents on the same engine without restarting it. To integrate your own agents, see SDK usage patterns.