skills/bratsos/polizy/polizy-troubleshooting

polizy-troubleshooting

SKILL.md

Polizy Troubleshooting

Debug authorization issues when things don't work as expected.

When to Apply

  • User says "permission check not working"
  • User says "user can't access X but should"
  • Error messages from polizy
  • User confused about why authorization behaves a certain way
  • check() returns false unexpectedly

Quick Diagnosis Flowchart

check() returns false unexpectedly
Is the relation in actionToRelations?
    │           │
   NO           YES
    │            │
    ▼            ▼
ADD IT      Is there a group relation?
             │           │
            NO           YES
             │            │
             ▼            ▼
    (Direct check)    Is user in group?
             │            │
             ▼           NO → Check addMember()
    Is tuple           YES
    present?            │
       │                ▼
      NO              Does group have permission?
       │                │
       ▼               NO → Check group's allow()
    Add with          YES
    allow()            │
                  Check depth limit

Common Issues

1. Relation Not Mapped to Action

Symptom: check() returns false even with permission granted.

// Schema
actionToRelations: {
  view: ["viewer"],  // "editor" missing!
  edit: ["editor"],
}

// Grant
await authz.allow({ who: alice, toBe: "editor", onWhat: doc });

// Check
await authz.check({ who: alice, canThey: "view", onWhat: doc }); // false!

Fix: Add relation to action's array:

actionToRelations: {
  view: ["viewer", "editor"],  // Now editors can view
  edit: ["editor"],
}

2. Missing Group Relation

Symptom: addMember() throws error.

// Schema missing group type
relations: {
  viewer: { type: "direct" },
}

await authz.addMember({ member: alice, group: team });
// Error: No group relation defined in schema

Fix: Add group relation:

relations: {
  viewer: { type: "direct" },
  member: { type: "group" },  // Add this
}

3. Missing Hierarchy Propagation

Symptom: Parent permission doesn't flow to children.

// Schema
relations: {
  parent: { type: "hierarchy" },
  viewer: { type: "direct" },
},
// Missing hierarchyPropagation!

await authz.setParent({ child: doc, parent: folder });
await authz.allow({ who: alice, toBe: "viewer", onWhat: folder });
await authz.check({ who: alice, canThey: "view", onWhat: doc }); // false!

Fix: Add hierarchyPropagation:

hierarchyPropagation: {
  view: ["view"],  // Now view propagates
}

4. User Not in Group

Symptom: Group has permission but user can't access.

Debug:

// Check group membership
const memberships = await authz.listTuples({
  subject: { type: "user", id: "alice" },
  relation: "member",
});
console.log("Alice's groups:", memberships);

Fix: Add user to group:

await authz.addMember({ member: alice, group: team });

5. Max Depth Exceeded

Symptom: Deep group chain returns false silently.

Detect:

const authz = new AuthSystem({
  storage,
  schema,
  throwOnMaxDepth: true,  // Throws instead of silent false
});

try {
  await authz.check({ who: alice, canThey: "view", onWhat: doc });
} catch (error) {
  if (error instanceof MaxDepthExceededError) {
    console.log("Depth exceeded at:", error.depth);
  }
}

Fix: Increase depth or reduce nesting:

const authz = new AuthSystem({
  storage,
  schema,
  defaultCheckDepth: 20,  // Increase from default 10
});

6. Time-Based Condition Not Valid

Symptom: Permission granted with when but check fails.

Debug:

const tuples = await authz.listTuples({
  subject: alice,
  object: doc,
});

for (const tuple of tuples) {
  console.log("Condition:", tuple.condition);
  if (tuple.condition?.validSince) {
    console.log("Starts:", tuple.condition.validSince);
  }
  if (tuple.condition?.validUntil) {
    console.log("Expires:", tuple.condition.validUntil);
  }
}

Common causes:

  • validSince is in the future
  • validUntil is in the past

Debugging Techniques

1. List All Tuples for Subject

const tuples = await authz.listTuples({
  subject: { type: "user", id: "alice" },
});

console.log("Alice's permissions:");
for (const tuple of tuples) {
  console.log(`  ${tuple.relation} on ${tuple.object.type}:${tuple.object.id}`);
}

2. List All Tuples for Object

const tuples = await authz.listTuples({
  object: { type: "document", id: "doc1" },
});

console.log("Permissions on doc1:");
for (const tuple of tuples) {
  console.log(`  ${tuple.subject.type}:${tuple.subject.id} is ${tuple.relation}`);
}

3. Trace Group Membership

async function traceGroupPath(userId: string) {
  const user = { type: "user", id: userId };
  const groups: string[] = [];

  const directMemberships = await authz.listTuples({
    subject: user,
    relation: "member",
  });

  for (const tuple of directMemberships) {
    groups.push(`${tuple.object.type}:${tuple.object.id}`);

    // Check nested groups
    const nestedMemberships = await authz.listTuples({
      subject: tuple.object,
      relation: "member",
    });

    for (const nested of nestedMemberships) {
      groups.push(`${nested.object.type}:${nested.object.id}`);
    }
  }

  return groups;
}

console.log("Group path:", await traceGroupPath("alice"));

4. Trace Hierarchy Path

async function traceHierarchyPath(objectType: string, objectId: string) {
  const path: string[] = [`${objectType}:${objectId}`];
  let current = { type: objectType, id: objectId };

  while (true) {
    const parentTuples = await authz.listTuples({
      subject: current,
      relation: "parent",
    });

    if (parentTuples.length === 0) break;

    const parent = parentTuples[0].object;
    path.push(`${parent.type}:${parent.id}`);
    current = parent;
  }

  return path;
}

console.log("Hierarchy:", await traceHierarchyPath("document", "doc1"));
// ["document:doc1", "folder:subfolder", "folder:root"]

5. Enable Logging

const debugLog: string[] = [];

const authz = new AuthSystem({
  storage,
  schema,
  logger: {
    warn: (msg) => {
      debugLog.push(msg);
      console.warn("[Polizy]", msg);
    },
  },
});

// After operations, check debugLog for warnings

Error Reference

Error Cause Fix
SchemaError: Relation "X" is not defined Using undefined relation Add relation to schema
SchemaError: No group relation defined Missing group type Add member: { type: "group" }
SchemaError: No hierarchy relation defined Missing hierarchy type Add parent: { type: "hierarchy" }
MaxDepthExceededError Group/hierarchy too deep Increase depth or flatten
ConfigurationError: storage is required Missing storage adapter Provide storage in constructor
ConfigurationError: schema is required Missing schema Provide schema in constructor

Anti-Patterns to Avoid

See ANTI-PATTERNS.md for detailed explanations:

  1. Duplicating permissions across users - Use groups
  2. Deep group nesting - Keep 2-3 levels
  3. Generic relation names - Use semantic names
  4. Checking after action - Check before
  5. Not handling authorization errors - Show feedback

References

Related Skills

Weekly Installs
3
Repository
bratsos/polizy
GitHub Stars
6
First Seen
Feb 3, 2026
Installed on
opencode3
gemini-cli3
antigravity3
claude-code3
github-copilot3
cursor3