Policies
Policies are the fundamental building blocks of Sentrie policy packs. They encapsulate business rules, define input data structures, and export decision outcomes that can be consumed by applications or other policies.
Policy Structure
Section titled “Policy Structure”Basic Requirements
Section titled “Basic Requirements”- Namespace: Every policy must belong to a namespace
- Rules: Must declare one or more rules for decision logic
- Export: Must export at least one decision (rule outcome)
Core Components
Section titled “Core Components”- Facts: Declare input data declarations with types and defaults with
factstatements - Variables: Intermediate calculations using
letstatements - Rules: Decision logic with conditions and outcomes with
rulestatements - Use: External TypeScript functions via
usestatements - Exports: Rule outcomes for external consumption with
exportstatements
Declaring Policies
Section titled “Declaring Policies”Basic Syntax
Section titled “Basic Syntax”namespace com/example/domain
policy policyName { // Policy body}Complete Example
Section titled “Complete Example”namespace com/example/auth
shape User { id!: string role!: string permissions!: list[string]}
shape Resource { id!: string type!: string owner!: string}
policy userAccess { use { verifySignature, isBusinessHours } from "./auth-utils.ts" as auth
fact user: User as currentUser fact resource: Resource as currentResource fact context?: Context as ctx default { "environment": "production" }
let isResourceOwner = user.id == resource.owner let hasValidSignature = auth.verifySignature(user.id, resource.id) let isWithinBusinessHours = auth.isBusinessHours()
rule canRead = default false when (resource.type == "document") { yield isResourceOwner and hasValidSignature }
rule canWrite = default false when (resource.type == "document") { yield isResourceOwner and hasValidSignature and isWithinBusinessHours }
rule canAccess = canRead or canWrite
export decision of canAccess}Policy Components
Section titled “Policy Components”Use Statements
Section titled “Use Statements”-- Using TypeScript functionsuse { function1, function2 } from "./utils.ts" as utilsuse { validateEmail } from "./validation.ts" as validatorsFact Declarations
Section titled “Fact Declarations”-- Required facts (default behavior, must be provided)fact user: User as currentUserfact resource: Resource as currentResource
-- Optional facts (marked with ?, can have defaults)fact context?: Context as ctx default { "key": "value" }fact config?: Config as settings default { "environment": "production" }Variable Declarations
Section titled “Variable Declarations”-- Simple calculationslet isAdmin = user.role == "admin"let totalPrice = item.price * quantity
-- Complex logiclet canAccess = user.active and (user.role == "admin" or user.permissions contains "read")Rule Declarations
Section titled “Rule Declarations”-- Basic rulerule canRead = default false { yield user.role == "admin"}
-- Conditional rulerule canWrite = default false when (user.active) { yield user.role == "admin" and user.verified}
-- Composite rulerule canAccess = canRead or canWriteExport Rule outcomes
Section titled “Export Rule outcomes”-- Export rule outcomeexport decision of canAccess
-- Export multiple outcomesexport decision of canReadexport decision of canWrite
-- Export with attachmentsexport decision of canAccess attach reason as "Access granted" attach level as user.roleexport decision of canRead attach permissions as user.permissions attach timestamp as currentTime()Import Rule outcomes of other policies
Section titled “Import Rule outcomes of other policies”-- Import rule from another policyrule externalRule = import decision of ruleName from com/example/other/policy with factName as expr
-- Examplerule userPermission = import decision of canAccess from com/example/auth/userAccess with user as currentUser
-- Accessing attachments from imported rulesrule authResult = import decision of canAccess from com/example/auth/userAccess with user as currentUserlet accessReason = authResult.reason -- Access the 'reason' attachmentlet accessLevel = authResult.level -- Access the 'level' attachmentlet userPermissions = authResult.permissions -- Access the 'permissions' attachmentRule Attachments
Section titled “Rule Attachments”Rule attachments allow you to include additional metadata with exported rule outcomes. This metadata can be accessed when the rule is imported by other policies or executed via the HTTP API.
Exporting with Decision Attachments
Section titled “Exporting with Decision Attachments”-- Basic attachment syntaxexport decision of ruleName (attach name as expression)*
-- Examplesexport decision of canAccess attach reason as "Access granted"
export decision of canRead attach permissions as user.permissions attach timestamp as currentTime()
export decision of canWrite attach level as user.role attach department as user.departmentImporting and Accessing Decision Attachments
Section titled “Importing and Accessing Decision Attachments”-- Import rule with attachmentsrule importedRule = import decision of ruleName from com/example/policy with factName as expr
-- Access attachments using field accessorslet attachmentValue = importedRule.attachmentName
-- Examplerule authResult = import decision of canAccess from com/example/auth/userAccess with user as currentUserlet accessReason = authResult.reason -- Access the 'reason' attachmentlet accessLevel = authResult.level -- Access the 'level' attachmentlet userPermissions = authResult.permissions -- Access the 'permissions' attachmentPractical Use Cases
Section titled “Practical Use Cases”-- Export with debugging informationexport decision of canAccess attach debugInfo as "User verified" attach timestamp as currentTime()
-- Export with business contextexport decision of canApprove attach approverLevel as user.role attach approvalLimit as user.maxAmount
-- Export with audit trailexport decision of canDelete attach auditReason as "Data retention policy" attach retentionDate as item.createdDateBest Practices for Attachments
Section titled “Best Practices for Attachments”Use Descriptive Names
Section titled “Use Descriptive Names”-- Good: Clear, descriptive attachment namesexport decision of canAccess attach accessReason as "User has admin role" attach accessLevel as user.role
-- Avoid: Generic or unclear namesexport decision of canAccess attach info as "ok" attach data as user.roleKeep Attachments Relevant
Section titled “Keep Attachments Relevant”-- Good: Only include necessary metadataexport decision of canApprove attach approverLevel as user.role attach approvalLimit as user.maxAmount
-- Avoid: Including unnecessary or sensitive dataexport decision of canApprove attach approverLevel as user.role attach userPassword as user.passwordUse Consistent Naming Conventions
Section titled “Use Consistent Naming Conventions”-- Good: Consistent naming patternexport decision of canRead attach readReason as "User has read permission" attach readLevel as user.role
export decision of canWrite attach writeReason as "User has write permission" attach writeLevel as user.role
-- Good: Use meaningful prefixesexport decision of canAccess attach accessReason as "Access granted" attach accessLevel as user.roleHandle Missing Attachments Gracefully
Section titled “Handle Missing Attachments Gracefully”-- Check if attachment exists before accessingrule authResult = import decision of canAccess from com/example/auth/userAccess with user as currentUser
let reason = authResult.reason is defined ? authResult.reason : "No reason provided"let level = authResult.level is defined ? authResult.level : "unknown"Use Attachments for Debugging
Section titled “Use Attachments for Debugging”-- Include debugging information in developmentexport decision of canAccess attach debugInfo as "User verified: " + user.id attach timestamp as currentTime()
-- In production, you might remove or simplify debug attachmentsexport decision of canAccess attach timestamp as currentTime()Document Attachment Usage
Section titled “Document Attachment Usage”-- Document what attachments are availablepolicy userAccess { -- Exports canAccess with attachments: -- - reason: Human-readable explanation -- - level: User's access level -- - timestamp: When decision was made export decision of canAccess attach reason as "Access granted" attach level as user.role attach timestamp as currentTime()}Avoid Overusing Attachments
Section titled “Avoid Overusing Attachments”-- Good: Focused, essential attachmentsexport decision of canAccess attach reason as "Access granted" attach level as user.role
-- Avoid: Too many attachments that clutter the interfaceexport decision of canAccess attach reason as "Access granted" attach level as user.role attach timestamp as currentTime() attach debugInfo as "User verified" attach sessionId as user.sessionId attach requestId as request.idUse Proper Alignment
Section titled “Use Proper Alignment”-- Good: Align attachments for readabilityexport decision of canAccess attach reason as "Access granted" attach level as user.role attach timestamp as currentTime()
-- Good: Align import statements for readabilityrule authResult = import decision of canAccess from com/example/auth/userAccess with user as currentUser
-- Avoid: Single line when it becomes too longexport decision of canAccess attach reason as "Access granted" attach level as user.role attach timestamp as currentTime() attach debugInfo as "User verified"Policy Interactions
Section titled “Policy Interactions”Rule Composition
Section titled “Rule Composition”-- Combine multiple rules
let isBusinessHours = ...
rule canRead = ...rule canWrite = ...rule complexAccess = { yield canRead and canWrite and isBusinessHours }
-- Conditional compositionrule conditionalAccess = { yield user.role == "admin" ? canAccess : canRead }Cross-Policy Dependencies
Section titled “Cross-Policy Dependencies”-- Import from another policyrule authResult = import decision of authenticate from com/example/auth/login with user as currentUser
-- Use imported resultrule canProceed = authResult and user.verifiedBest Practices
Section titled “Best Practices”Clear Naming
Section titled “Clear Naming”-- Good: Descriptive policy and rule namespolicy userAccessControl { rule canReadDocument = default false { /* ... */ } rule canWriteDocument = default false { /* ... */ }}
-- Avoid: Generic namespolicy policy1 { rule rule1 = default false { /* ... */ }}Logical Organization
Section titled “Logical Organization”-- Group related rulespolicy documentAccess { rule canRead = default false { /* read logic */ } rule canWrite = default false { /* write logic */ } rule canDelete = default false { /* delete logic */ }
rule canAccess = canRead or canWrite or canDelete}Error Handling
Section titled “Error Handling”-- Provide sensible defaultsrule canAccess = default false when (user is defined and resource is defined) { yield user.active and user.verified}Documentation
Section titled “Documentation”-- Document complex logicpolicy complexBusinessLogic { -- This rule implements the company's access control policy -- Users must be active, verified, and have appropriate role rule canAccess = default false { yield user.active and user.verified and user.role in ["admin", "manager"] }}