API ReferenceGovernance
Separation of Duties
Static rules that prevent toxic combinations of permissions.
RPCs
| Method | Path |
|---|---|
| CreateSoDRule | POST /v1/governance/sodRules |
| ListSoDRules | GET /v1/governance/sodRules |
| DeleteSoDRule | DELETE /v1/governance/sodRules/{id} |
| CheckSoDViolation | POST /v1/governance/sodRules:check |
| ListSoDViolations | GET /v1/governance/sodViolations |
Base URL: https://<Domain id="api"/>
Authentication: Bearer token with governance.sod:write (create/delete) or :read (list/check).
Rule shape
An SoD rule defines two sets of permissions that cannot both be held by the same identity:
{
"id": "sod_01H7X3K9Q1",
"name"
enforcement options:
WARN-- violation recorded, Keto write proceeds.BLOCK-- violation recorded, Keto write rejected withAVNOLOGY_AUTH_102.REQUIRE_APPROVAL-- write converted into an access request.
Create a rule
body := strings.NewReader(`{
"name": "PO creator vs approver",
"conflicting_sets": [
{"namespace":"purchase_orders","relation":"creator"},
{"namespace":"purchase_orders","relation":"approver"}
],
"severity": "HIGH",
"enforcement": "BLOCK"
}`)
import httpx, os
httpx.post(
"https://api-id.avnology.net/v1/governance/sodRules",
json={
"name": "PO creator vs approver",
"conflicting_sets": [
{"namespace": "purchase_orders"
curl -X POST "https://api-id.avnology.net/v1/governance/sodRules" \
-H "Authorization: Bearer $AVNOLOGY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "PO creator vs approver",
"conflicting_sets": [
{ "namespace": "purchase_orders", "relation": "creator" },
{ "namespace": "purchase_orders", "relation": "approver" }
],
"severity": "HIGH",
"enforcement": "BLOCK"
}'body := strings.NewReader(`{
"name": "PO creator vs approver",
"conflicting_sets": [
{"namespace":"purchase_orders","relation":"creator"},
{"namespace":"purchase_orders","relation":"approver"}
],
"severity": "HIGH",
"enforcement": "BLOCK"
}`)
import httpx, os
httpx.post(
"https://api-id.avnology.net/v1/governance/sodRules",
json={
"name": "PO creator vs approver",
"conflicting_sets": [
{"namespace": "purchase_orders"
curl -X POST "https://api-id.avnology.net/v1/governance/sodRules" \
-H "Authorization: Bearer $AVNOLOGY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "PO creator vs approver",
"conflicting_sets": [
{ "namespace": "purchase_orders", "relation": "creator" },
{ "namespace": "purchase_orders", "relation": "approver" }
],
"severity": "HIGH",
"enforcement": "BLOCK"
}'body := strings.NewReader(`{
"name": "PO creator vs approver",
"conflicting_sets": [
{"namespace":"purchase_orders","relation":"creator"},
{"namespace":"purchase_orders","relation":"approver"}
],
"severity": "HIGH",
"enforcement": "BLOCK"
}`)
import httpx, os
httpx.post(
"https://api-id.avnology.net/v1/governance/sodRules",
json={
"name": "PO creator vs approver",
"conflicting_sets": [
{"namespace": "purchase_orders"
curl -X POST "https://api-id.avnology.net/v1/governance/sodRules" \
-H "Authorization: Bearer $AVNOLOGY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "PO creator vs approver",
"conflicting_sets": [
{ "namespace": "purchase_orders", "relation": "creator" },
{ "namespace": "purchase_orders", "relation": "approver" }
],
"severity": "HIGH",
"enforcement": "BLOCK"
}'body := strings.NewReader(`{
"name": "PO creator vs approver",
"conflicting_sets": [
{"namespace":"purchase_orders","relation":"creator"},
{"namespace":"purchase_orders","relation":"approver"}
],
"severity": "HIGH",
"enforcement": "BLOCK"
}`)
import httpx, os
httpx.post(
"https://api-id.avnology.net/v1/governance/sodRules",
json={
"name": "PO creator vs approver",
"conflicting_sets": [
{"namespace": "purchase_orders"
Pre-flight check
POST /v1/governance/sodRules:check lets you test a proposed permission grant against every SoD rule without writing it. Response includes a violations[] array with rule IDs and severities.