This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
backendcritical PriorityID: openfga-subgraph-memoization-depth-limiting

OpenFGA Transitive Depth Limiting and Sub-Graph Memoization Guardrails

Enforces strict recursion depth boundaries and request-scoped sub-graph memoization during OpenFGA relationship-based access control (ReBAC) evaluations. Prevents algorithmic complexity denial-of-service (Graph-DoS) and tail-latency degradation caused by cyclic hierarchies, deep transitive inheritance, and redundant node expansions. Guarantees deterministic evaluation cost and O(1) retrieval for previously traversed authorization paths.

Prohibited Anti-Pattern: Unbounded Recursive Traversal and Duplicate Sub-Tree Fan-Out in Relationship Checks

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: OpenFGA Transitive Depth Limiting and Sub-Graph Memoization Guardrails - Enforces strict recursion depth boundaries and request-scoped sub-graph memoization during OpenFGA relationship-based access control (ReBAC) evaluations. Prevents algorithmic complexity denial-of-service (Graph-DoS) and tail-latency degradation caused by cyclic hierarchies, deep transitive inheritance, and redundant node expansions. Guarantees deterministic evaluation cost and O(1) retrieval for previously traversed authorization paths.
globs: src/auth/**/*.ts, src/services/fga/**/*.ts, src/services/authz/**/*.ts, src/middleware/authorize*.ts
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/openfga-subgraph-memoization-depth-limiting"
---

# OpenFGA Transitive Depth Limiting and Sub-Graph Memoization Guardrails

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Unbounded Recursive Traversal and Duplicate Sub-Tree Fan-Out in Relationship Checks

## System Constraints
You are a Principal Security and Performance Architect enforcing zero-trust, resilient OpenFGA relationship evaluations. You must eliminate all unbounded transitive relationship traversals and redundant sub-graph evaluations. Mandate strict request-scoped memoization keys, hard recursion limits, cycle detection, and fail-closed evaluation boundaries across all client-side and service-side authorization graph navigations.

## Strict Guidelines
- Engineers MUST ALWAYS enforce a deterministic maximum traversal depth (`maxDepth` <= 15 by default, hard ceiling at 25) when traversing nested tuples or custom expansion graphs.
- Engineers MUST NOT execute raw, unmemoized parallel OpenFGA `check` operations across shared entity sub-graphs within the lifecycle of a single incoming request.
- Services MUST implement a request-scoped memoization cache (using canonical tuple keys `${user}#${relation}@${object}`) to deduplicate concurrent or sequential branch evaluations.
- Traversal engines MUST maintain a `visited` set per trace path to immediately abort cyclic relationships before exhausting the call stack or reaching engine timeouts.
- Code SHOULD configure OpenFGA SDK clients with explicitly defined read timeouts, concurrency thresholds, and contextual tuple boundaries to prevent resource exhaustion under deep fan-outs.
- Components NEVER fall back to permissive defaults (`allow = true`) when depth limits are reached or when sub-graph evaluation cycles are detected; all graph boundary trips MUST fail closed.

## Bad vs Good Implementations
### Unbounded Transitive Permission Resolution vs. Memoized and Depth-Bounded Traversal

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: This implementation lacks recursion depth limiting, cycle detection, and sub-graph memoization. If nodes in the relationship graph form a cycle or exhibit high fan-out with shared ancestors (diamond dependency), the service incurs an exponential explosion of network calls, saturates OpenFGA storage, and causes catastrophic tail latency.
```typescript
import { OpenFgaClient } from '@openfga/sdk';

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

export class UnsafeAuthorizationService {
  constructor(private readonly fgaClient: OpenFgaClient) {}

  // ANTI-PATTERN: Unbounded recursion, no memoization, no cycle tracking.
  // A cyclic group structure (A -> B -> A) or deep nesting causes stack overflow / graph exhaustion.
  async canAccess(options: CheckOptions): Promise<boolean> {
    const response = await this.fgaClient.check({
      user: options.user,
      relation: options.relation,
      object: options.object,
    });

    if (response.allowed) {
      return true;
    }

    // Client-side expansion without memoization or depth limiting
    const subRelations = await this.findInheritedObjects(options.object);
    for (const parent of subRelations) {
      // Redundantly checks identical paths on fan-out; easily creates Graph-DoS
      const parentAllowed = await this.canAccess({
        user: options.user,
        relation: options.relation,
        object: parent,
      });
      if (parentAllowed) return true;
    }

    return false;
  }

  private async findInheritedObjects(object: string): Promise<string[]> {
    // Arbitrary unmemoized relationship lookup
    return [];
  }
}
```

#### Production Standard (Correct)
> **Standard**: The hardened implementation guarantees O(V + E) bounded traversal by passing a shared request-scoped memoization map and a branch-scoped cycle detection set. In-flight checks for identical canonical keys are reused across fan-out branches, and depth is strictly constrained by MAX_TRANSITIVE_DEPTH with a fail-closed response.
```typescript
import { OpenFgaClient } from '@openfga/sdk';

interface CheckContext {
  user: string;
  relation: string;
  object: string;
  currentDepth?: number;
  memo?: Map<string, Promise<boolean>>;
  visited?: Set<string>;
}

const MAX_TRANSITIVE_DEPTH = 15;

export class HardenedAuthorizationService {
  constructor(private readonly fgaClient: OpenFgaClient) {}

  public async canAccess(ctx: CheckContext): Promise<boolean> {
    const memo = ctx.memo ?? new Map<string, Promise<boolean>>();
    const visited = ctx.visited ?? new Set<string>();
    const depth = ctx.currentDepth ?? 0;

    // 1. Enforce depth ceiling (Fail Closed)
    if (depth > MAX_TRANSITIVE_DEPTH) {
      return false;
    }

    const canonicalKey = `${ctx.user}#${ctx.relation}@${ctx.object}`;

    // 2. Cycle Detection: abort if current branch re-enters an active path
    if (visited.has(canonicalKey)) {
      return false;
    }

    // 3. Sub-Graph Memoization: return shared promise for in-flight/completed sub-paths
    const cachedResult = memo.get(canonicalKey);
    if (cachedResult !== undefined) {
      return cachedResult;
    }

    const evaluationPromise = (async () => {
      const branchVisited = new Set(visited).add(canonicalKey);

      const response = await this.fgaClient.check({
        user: ctx.user,
        relation: ctx.relation,
        object: ctx.object,
      });

      if (response.allowed) {
        return true;
      }

      // Transitive fallback with bounded depth and path tracking
      const parents = await this.resolveParentNodes(ctx.object);
      for (const parent of parents) {
        const isGranted = await this.canAccess({
          user: ctx.user,
          relation: ctx.relation,
          object: parent,
          currentDepth: depth + 1,
          memo,
          visited: branchVisited,
        });
        if (isGranted) return true;
      }

      return false;
    })();

    memo.set(canonicalKey, evaluationPromise);
    return evaluationPromise;
  }

  private async resolveParentNodes(object: string): Promise<string[]> {
    // Canonical parent discovery logic bounded by request context
    return [];
  }
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: OpenFGA Transitive Depth Limiting and Sub-Graph Memoization Guardrails

OpenFGA Transitive Depth Limiting and Sub-Graph Memoization Guardrails defines an industrial-grade behavioral standard for backend engineering. Enforces strict recursion depth boundaries and request-scoped sub-graph memoization during OpenFGA relationship-based access control (ReBAC) evaluations. Prevents algorithmic complexity denial-of-service (Graph-DoS) and tail-latency degradation caused by cyclic hierarchies, deep transitive inheritance, and redundant node expansions. Guarantees deterministic evaluation cost and O(1) retrieval for previously traversed authorization paths. 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 Duplicate Sub-Tree Fan-Out in Relationship Checks 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 Duplicate Sub-Tree Fan-Out in Relationship ChecksCritical
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 deterministic maximum traversal depth (`maxDepth` <= 15 by default, hard ceiling at 25) when traversing nested tuples or custom expansion graphs.

Engineers MUST ALWAYS enforce a deterministic maximum traversal depth (`maxDepth` <= 15 by default, hard ceiling at 25) when traversing nested tuples or custom expansion graphs.

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 NOT execute raw, unmemoized parallel OpenFGA `check` operations across shared entity sub-graphs within the lifecycle of a single incoming request.

Engineers MUST NOT execute raw, unmemoized parallel OpenFGA `check` operations across shared entity sub-graphs within the lifecycle of a single incoming request.

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 implement a request-scoped memoization cache (using canonical tuple keys `${user}#${relation}@${object}`) to deduplicate concurrent or sequential branch evaluations.

Services MUST implement a request-scoped memoization cache (using canonical tuple keys `${user}#${relation}@${object}`) to deduplicate concurrent or sequential branch 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.
4

Traversal engines MUST maintain a `visited` set per trace path to immediately abort cyclic relationships before exhausting the call stack or reaching engine timeouts.

Traversal engines MUST maintain a `visited` set per trace path to immediately abort cyclic relationships before exhausting the call stack or reaching engine timeouts.

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

Code SHOULD configure OpenFGA SDK clients with explicitly defined read timeouts, concurrency thresholds, and contextual tuple boundaries to prevent resource exhaustion under deep fan-outs.

Code SHOULD configure OpenFGA SDK clients with explicitly defined read timeouts, concurrency thresholds, and contextual tuple boundaries to prevent resource exhaustion under deep fan-outs.

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

Components NEVER fall back to permissive defaults (`allow = true`) when depth limits are reached or when sub-graph evaluation cycles are detected

Components NEVER fall back to permissive defaults (`allow = true`) when depth limits are reached or when sub-graph evaluation cycles are detected; all graph boundary trips MUST fail closed.

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 Transitive Depth Limiting and Sub-Graph Memoization Guardrails critical for AI coding assistants?

Enforces strict recursion depth boundaries and request-scoped sub-graph memoization during OpenFGA relationship-based access control (ReBAC) evaluations. Prevents algorithmic complexity denial-of-service (Graph-DoS) and tail-latency degradation caused by cyclic hierarchies, deep transitive inheritance, and redundant node expansions. Guarantees deterministic evaluation cost and O(1) retrieval for previously traversed authorization paths. 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 Duplicate Sub-Tree Fan-Out in Relationship Checks) 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