Policy

A PolicySpec defines who can do what, when, and under what conditions. ContractSpec uses attribute-based access control (ABAC) to enforce policies across your entire application—from API endpoints to UI components.

Why policies matter

Traditional access control relies on roles (RBAC), which can become unwieldy as applications grow. ABAC is more flexible—it evaluates policies based on attributes of the user, resource, action, and context.

ContractSpec's policy engine ensures that access control is consistent across all surfaces. You don't have to remember to add authorization checks in every API endpoint or UI component—the Policy Decision Point enforces policies automatically.

Policy structure

A PolicySpec contains one or more rules. Each rule has:

  • Effect – PERMIT, DENY, or REDACT
  • Condition – A boolean expression that determines when the rule applies
  • Scope – Which resources, actions, or fields the rule applies to
  • Priority – Rules are evaluated in priority order; the first matching rule wins

Example PolicySpec

Here's a policy that controls access to customer data in TypeScript:

import { definePolicy } from '@lssm/lib.contracts';

export const CustomerDataAccess = definePolicy({
  meta: {
    name: 'customer.data.access',
    version: 1,
  },
  rules: [
    {
      id: 'admin-full-access',
      priority: 100,
      effect: 'PERMIT',
      condition: (ctx) => ctx.user.role === 'admin',
      scope: {
        resources: ['customer'],
        actions: ['read', 'write', 'delete'],
      },
    },
    {
      id: 'sales-read-assigned',
      priority: 200,
      effect: 'PERMIT',
      condition: (ctx) => 
        ctx.user.role === 'sales' && 
        ctx.resource.assignedTo === ctx.user.id,
      scope: {
        resources: ['customer'],
        actions: ['read', 'write'],
      },
    },
    {
      id: 'support-read-redacted',
      priority: 300,
      effect: 'REDACT',
      condition: (ctx) => ctx.user.role === 'support',
      scope: {
        resources: ['customer'],
        actions: ['read'],
      },
      redactFields: ['creditCard', 'ssn', 'bankAccount'],
    },
  ],
});

Attributes

Policy conditions can reference attributes from four categories:

User attributes

user.id, user.role, user.groups, user.department, custom attributes

Resource attributes

resource.type, resource.owner, resource.sensitivity, custom attributes

Action attributes

action (read, write, delete, execute, export, etc.)

Context attributes

time.hour, time.dayOfWeek, request.ipAddress, request.userAgent

Data classification

You can tag fields with sensitivity levels in your CapabilitySpecs and DataViewSpecs:

fields:
  - name: email
    type: string
    sensitivity: PII
  - name: creditCard
    type: string
    sensitivity: PII
    encrypted: true
  - name: diagnosis
    type: string
    sensitivity: PHI
  - name: salary
    type: number
    sensitivity: confidential

Policies can then reference these tags to enforce blanket rules like "support staff cannot see PII" or "PHI can only be accessed from approved IP addresses."

Testing policies

ContractSpec provides tools for testing policies before deployment:

  • Policy simulator – Test how policies evaluate for different users and scenarios
  • Coverage analysis – Identify resources or actions that aren't covered by any policy
  • Conflict detection – Find rules that might conflict or produce unexpected results
  • Audit mode – Run policies in audit-only mode to see what would be blocked without actually blocking it

Best practices

  • Start with a deny-by-default policy—explicitly permit what should be allowed.
  • Use clear, descriptive rule IDs that explain what the rule does.
  • Set priorities carefully to ensure rules are evaluated in the right order.
  • Test policies thoroughly with realistic user scenarios before deploying.
  • Monitor policy decisions in production using audit logs.
  • Review and update policies regularly as your application and requirements evolve.