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