This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
backendcritical PriorityID: openfga-rebac-cycle-detection-bounded-depth

OpenFGA Zanzibar-Style Cycle Detection & Bounded Depth Evaluation Standard

Enforces deterministic cycle detection, breadth/depth-budget constraints, and visited-node memoization during ReBAC relationship-tuple evaluation. Prevents Denial-of-Service, thread starvation, and call-stack exhaustion caused by circular group memberships, nested folder hierarchies, and unbounded userset graph expansions.

Prohibited Anti-Pattern: Unbounded Recursive Traversal and Missing Visited-State Tracking in Graph Authorization Queries

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: OpenFGA Zanzibar-Style Cycle Detection & Bounded Depth Evaluation Standard - Enforces deterministic cycle detection, breadth/depth-budget constraints, and visited-node memoization during ReBAC relationship-tuple evaluation. Prevents Denial-of-Service, thread starvation, and call-stack exhaustion caused by circular group memberships, nested folder hierarchies, and unbounded userset graph expansions.
globs: src/auth/**/*.ts, src/authorization/**/*.ts, src/services/fga/**/*.ts, src/security/rebac/**/*.ts
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/openfga-rebac-cycle-detection-bounded-depth"
---

# OpenFGA Zanzibar-Style Cycle Detection & Bounded Depth Evaluation Standard

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Unbounded Recursive Traversal and Missing Visited-State Tracking in Graph Authorization Queries

## System Constraints
You are a Principal Engineer enforcing the OpenFGA Zanzibar-Style Cycle Detection & Bounded Depth Evaluation Standard. Mandate that all ReBAC and graph-based authorization algorithms implement hard depth budgets, branch-level path tracking to detect cycles, and safe short-circuiting semantics. Prevent any unbounded recursion or unmemoized sub-problem expansion.

## Strict Guidelines
- Engineers MUST ALWAYS enforce a strictly bounded depth budget (e.g., maximum depth of 25) across all recursive userset and tuple evaluations.
- Engineers MUST ALWAYS track traversal history using an immutable or branch-scoped visited set containing composite keys of the form `${targetType}:${targetId}#${relation}`.
- Services MUST gracefully short-circuit circular branches with a safe negative result (or dedicated cycle diagnostic) rather than permitting recurring evaluation of previously traversed nodes.
- Services MUST throw a typed, operational error (e.g., MaxDepthExceededException) when evaluation depth reaches the configured limit to trip circuit breakers and alert telemetry.
- Engineers MUST NOT implement ReBAC graph traversal using bare, unconstrained recursive functions or Promise.all expansions without concurrency throttling and depth propagation.
- Code SHOULD leverage sub-problem memoization (resolving identical canonical check requests within a single query context) to optimize multi-hop evaluation lattices.

## Bad vs Good Implementations
### Zanzibar Relationship Traversal with Depth Bounds & Cycle Guardrails

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The check method lacks both recursion depth limits and visited-node cycle detection. If group A belongs to group B and group B belongs to group A, an authorization check triggers infinite recursive calls, saturating CPU cycles and eventually crashing the Node.js process with a RangeError: Maximum call stack size exceeded.
```typescript
import { TupleStore } from './tuple-store';

interface CheckRequest {
  user: string;
  relation: string;
  object: string;
}

export class NaiveRebacResolver {
  constructor(private store: TupleStore) {}

  // VULNERABLE: Infinite recursion on circular memberships (e.g., group:A in group:B, group:B in group:A)
  // Causes call stack exhaustion, unhandled promise rejections, and server thread lock.
  async check(req: CheckRequest): Promise<boolean> {
    const directTuples = await this.store.find({
      object: req.object,
      relation: req.relation,
    });

    for (const tuple of directTuples) {
      if (tuple.user === req.user) {
        return true;
      }
      // Blindly recurse on usersets without cycle detection or depth tracking
      if (tuple.user.includes('#')) {
        const [nestedObject, nestedRelation] = tuple.user.split('#');
        const hasAccess = await this.check({
          user: req.user,
          relation: nestedRelation,
          object: nestedObject,
        });
        if (hasAccess) return true;
      }
    }

    return false;
  }
}
```

#### Production Standard (Correct)
> **Standard**: The refactored implementation uses an immutable path-tracking context (`visitedPath`) to identify cyclic references at zero runtime risk, terminating cyclical evaluations immediately. It also introduces a hard maximum depth boundary (`maxDepth: 25`) with a typed `MaxDepthExceededError`, preventing resource exhaustion and unbounded traversal attacks.
```typescript
import { TupleStore, Tuple } from './tuple-store';

export class MaxDepthExceededError extends Error {
  constructor(depth: number, target: string) {
    super(`Evaluation exceeded max depth of ${depth} while resolving ${target}`);
    this.name = 'MaxDepthExceededError';
  }
}

interface CheckRequest {
  user: string;
  relation: string;
  object: string;
}

interface EvaluationContext {
  currentDepth: number;
  maxDepth: number;
  visitedPath: ReadonlySet<string>;
}

export class ResilientRebacResolver {
  private static readonly DEFAULT_MAX_DEPTH = 25;

  constructor(private readonly store: TupleStore) {}

  async check(req: CheckRequest): Promise<boolean> {
    return this.evaluate(req, {
      currentDepth: 0,
      maxDepth: ResilientRebacResolver.DEFAULT_MAX_DEPTH,
      visitedPath: new Set<string>(),
    });
  }

  private async evaluate(req: CheckRequest, ctx: EvaluationContext): Promise<boolean> {
    if (ctx.currentDepth > ctx.maxDepth) {
      throw new MaxDepthExceededError(ctx.maxDepth, `${req.object}#${req.relation}`);
    }

    const nodeKey = `${req.object}#${req.relation}`;
    // Cycle Detection: If already visited in the current resolution chain, abort this branch
    if (ctx.visitedPath.has(nodeKey)) {
      return false;
    }

    const branchVisited = new Set(ctx.visitedPath).add(nodeKey);
    const tuples = await this.store.find({ object: req.object, relation: req.relation });

    for (const tuple of tuples) {
      if (tuple.user === req.user) {
        return true;
      }

      // Computed userset resolution (e.g., user is 'group:eng#member')
      if (tuple.user.includes('#')) {
        const [nestedObject, nestedRelation] = tuple.user.split('#');
        const granted = await this.evaluate(
          {
            user: req.user,
            relation: nestedRelation,
            object: nestedObject,
          },
          {
            currentDepth: ctx.currentDepth + 1,
            maxDepth: ctx.maxDepth,
            visitedPath: branchVisited,
          }
        );

        if (granted) return true;
      }
    }

    return false;
  }
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: OpenFGA Zanzibar-Style Cycle Detection & Bounded Depth Evaluation Standard

OpenFGA Zanzibar-Style Cycle Detection & Bounded Depth Evaluation Standard defines an industrial-grade behavioral standard for backend engineering. Enforces deterministic cycle detection, breadth/depth-budget constraints, and visited-node memoization during ReBAC relationship-tuple evaluation. Prevents Denial-of-Service, thread starvation, and call-stack exhaustion caused by circular group memberships, nested folder hierarchies, and unbounded userset graph expansions. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Unbounded Recursive Traversal and Missing Visited-State Tracking in Graph Authorization Queries Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftUnbounded Recursive Traversal and Missing Visited-State Tracking in Graph Authorization QueriesCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS enforce a strictly bounded depth budget (e.g., maximum depth of 25) across all recursive userset and tuple evaluations.

Engineers MUST ALWAYS enforce a strictly bounded depth budget (e.g., maximum depth of 25) across all recursive userset and tuple evaluations.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST ALWAYS track traversal history using an immutable or branch-scoped visited set containing composite keys of the form `${targetType}

Engineers MUST ALWAYS track traversal history using an immutable or branch-scoped visited set containing composite keys of the form `${targetType}:${targetId}#${relation}`.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Services MUST gracefully short-circuit circular branches with a safe negative result (or dedicated cycle diagnostic) rather than permitting recurring evaluation of previously traversed nodes.

Services MUST gracefully short-circuit circular branches with a safe negative result (or dedicated cycle diagnostic) rather than permitting recurring evaluation of previously traversed nodes.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Services MUST throw a typed, operational error (e.g., MaxDepthExceededException) when evaluation depth reaches the configured limit to trip circuit breakers and alert telemetry.

Services MUST throw a typed, operational error (e.g., MaxDepthExceededException) when evaluation depth reaches the configured limit to trip circuit breakers and alert telemetry.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Engineers MUST NOT implement ReBAC graph traversal using bare, unconstrained recursive functions or Promise.all expansions without concurrency throttling and depth propagation.

Engineers MUST NOT implement ReBAC graph traversal using bare, unconstrained recursive functions or Promise.all expansions without concurrency throttling and depth propagation.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
6

Code SHOULD leverage sub-problem memoization (resolving identical canonical check requests within a single query context) to optimize multi-hop evaluation lattices.

Code SHOULD leverage sub-problem memoization (resolving identical canonical check requests within a single query context) to optimize multi-hop evaluation lattices.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is OpenFGA Zanzibar-Style Cycle Detection & Bounded Depth Evaluation Standard critical for AI coding assistants?

Enforces deterministic cycle detection, breadth/depth-budget constraints, and visited-node memoization during ReBAC relationship-tuple evaluation. Prevents Denial-of-Service, thread starvation, and call-stack exhaustion caused by circular group memberships, nested folder hierarchies, and unbounded userset graph expansions. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Unbounded Recursive Traversal and Missing Visited-State Tracking in Graph Authorization Queries) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

1 Rule in Stack

OpenFGA