Skip to Content
DocsUse CasesSegregation of Duties

Segregation of Duties

Segregation of duties (SOD) prevents any single agent from accumulating conflicting permissions. LODE encodes duty policies as verified binaries — the enforcement rules are tamper-proof and can be distributed alongside agent manifests.

Schema: DutyPolicy

duty-policy-v1.lode
{ "schema": "lode", "version": 1, "name": "DutyPolicy", "fields": [ { "id": 1, "name": "policyName", "type": "string", "required": true }, { "id": 2, "name": "roles", "type": "string[]", "required": true }, { "id": 3, "name": "permissions", "type": "map<string,string>", "required": true }, { "id": 4, "name": "conflictPairs", "type": "string[]", "required": true }, { "id": 5, "name": "requireApproval","type": "bool", "required": true }, { "id": 6, "name": "minReviewers", "type": "string", "required": false }, { "id": 7, "name": "constraints", "type": "map<string,string>", "required": false } ] }

Example: Financial SOD Policy

finops-sod.json
{ "policyName": "financial-operations-sod", "roles": ["submitter", "approver", "auditor"], "permissions": { "submitter:create-transaction": "allow", "submitter:approve-transaction": "deny", "approver:create-transaction": "deny", "approver:approve-transaction": "allow", "auditor:create-transaction": "deny", "auditor:approve-transaction": "deny", "auditor:view-audit-log": "allow" }, "conflictPairs": ["submitter:approver", "approver:auditor"], "requireApproval": true, "minReviewers": "2", "constraints": { "maxTransactionAmount": "10000", "escalationThreshold": "5000" } }
$ lodec compile finops-sod.json -s duty-policy-v1.lode -o policies/finops-sod.vein Compiled finops-sod.json policies/finops-sod.vein (312 bytes) Fingerprint: b8c9d0e1...

Enforcement

The SOD check runs before every agent action:

function checkDutyPolicy( policy: DutyPolicy, agentRole: string, action: string ): { allowed: boolean; reason: string } { // 1. Check permission const key = `${agentRole}:${action}`; const perm = policy.permissions[key]; if (perm === "deny") { return { allowed: false, reason: `Role "${agentRole}" denied action "${action}"` }; } if (!perm) { return { allowed: false, reason: `No permission defined for ${key}` }; } // 2. Check conflict pairs — agent must not hold conflicting roles for (const pair of policy.conflictPairs) { const [roleA, roleB] = pair.split(":"); if (agentRole === roleA || agentRole === roleB) { // Verify agent doesn't also hold the conflicting role } } return { allowed: true, reason: "Permitted" }; }

Verified Policy Distribution

Policies are distributed as Vein binaries with fingerprints. Each agent verifies the policy before enforcing it:

async function loadPolicy(policyUrl: string): Promise<DutyPolicy> { const res = await fetch(policyUrl); const vein = new Uint8Array(await res.arrayBuffer()); const expected = res.headers.get("X-Lode-Fingerprint"); const schema = loadLode("duty-policy-v1.lode"); if (!verify(vein, expected, schema)) { throw new Error("Policy verification failed — refusing to enforce unverified policy"); } return decode(vein, schema); }

Audit Trail

Because policies are fingerprinted, the audit log records exactly which policy version was active for every decision:

2026-03-15T10:00:00Z b8c9d0e1... submitter:create-transaction → allow 2026-03-15T10:00:01Z b8c9d0e1... submitter:approve-transaction → deny (SOD violation) 2026-03-15T10:05:00Z b8c9d0e1... approver:approve-transaction → allow (2 reviewers confirmed)
Last updated on