> ## Documentation Index
> Fetch the complete documentation index at: https://www.osohq.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Local Authorization

> Perform authorization checks directly against your database for better performance.

export const Languages = {
  JAVASCRIPT: "js",
  PYTHON: "py",
  RUBY: "rb",
  JAVA: "java",
  DOTNET: "dotnet",
  GO: "go",
  SHELL: "shell",
  MACOS: "macos",
  WINDOWS: "windows",
  BASH: "bash",
  CMD: "cmd",
  TYPESCRIPT: "ts",
  HTML: "html",
  CSS: "css",
  JSON: "json",
  YAML: "yaml",
  XML: "xml"
};

export const CodeTab = ({title, language, children, disabled = false}) => {
  const getLanguageDisplayName = language => {
    const displayNames = {
      [Languages.JAVASCRIPT]: "JavaScript",
      [Languages.PYTHON]: "Python",
      [Languages.RUBY]: "Ruby",
      [Languages.JAVA]: "Java",
      [Languages.DOTNET]: ".NET",
      [Languages.GO]: "Go",
      [Languages.SHELL]: "Shell",
      [Languages.MACOS]: "MacOS / Linux",
      [Languages.WINDOWS]: "Windows",
      [Languages.CMD]: "Command Prompt",
      [Languages.TYPESCRIPT]: "TypeScript",
      [Languages.HTML]: "HTML",
      [Languages.CSS]: "CSS",
      [Languages.JSON]: "JSON",
      [Languages.YAML]: "YAML",
      [Languages.XML]: "XML"
    };
    return displayNames[language] || language || "Code";
  };
  const displayTitle = title || getLanguageDisplayName(language);
  if (!displayTitle) {
    console.warn('CodeTab component requires either a title prop or language prop');
    return null;
  }
  if (language && typeof language !== 'string') {
    console.warn('CodeTab language prop must be a string');
  }
  if (title && typeof title !== 'string') {
    console.warn('CodeTab title prop must be a string');
  }
  if (disabled && typeof disabled !== 'boolean') {
    console.warn('CodeTab disabled prop must be a boolean');
  }
  return React.createElement(React.Fragment, null, children);
};

export const CodeTabs = ({children, defaultTab = 0, className = '', syncLanguages = true, onTabChange, 'aria-label': ariaLabel = 'Code examples'}) => {
  const {useState, useMemo, useEffect, useCallback, useId, Children, isValidElement} = React;
  const baseId = useId();
  const getLanguageDisplayName = useCallback(language => {
    const displayNames = {
      [Languages.JAVASCRIPT]: "JavaScript",
      [Languages.PYTHON]: "Python",
      [Languages.RUBY]: "Ruby",
      [Languages.JAVA]: "Java",
      [Languages.DOTNET]: ".NET",
      [Languages.GO]: "Go",
      [Languages.SHELL]: "Shell",
      [Languages.MACOS]: "MacOS / Linux",
      [Languages.WINDOWS]: "Windows",
      [Languages.CMD]: "Command Prompt",
      [Languages.TYPESCRIPT]: "TypeScript",
      [Languages.HTML]: "HTML",
      [Languages.CSS]: "CSS",
      [Languages.JSON]: "JSON",
      [Languages.YAML]: "YAML",
      [Languages.XML]: "XML"
    };
    return displayNames[language] || language || "Code";
  }, []);
  const LANGUAGE_PREF_KEY = useMemo(() => 'mintlify-tabs-language-preference', []);
  const LANGUAGE_CHANGE_EVENT = useMemo(() => 'mintlify-tabs-language-change', []);
  const findMatchingLanguageTab = useCallback((tabs, targetLanguage) => {
    if (!targetLanguage?.trim() || !Array.isArray(tabs) || tabs.length === 0) return -1;
    const normalizedTarget = targetLanguage.toLowerCase().trim();
    return tabs.findIndex(tab => tab?.language?.toLowerCase().trim() === normalizedTarget);
  }, []);
  const getLanguagePreference = useCallback(() => {
    if (typeof window === 'undefined' || !window.localStorage) return null;
    try {
      const stored = localStorage.getItem(LANGUAGE_PREF_KEY);
      if (!stored) return null;
      const parsed = JSON.parse(stored);
      if (typeof parsed === 'object' && parsed.language && typeof parsed.language === 'string') {
        return parsed;
      }
      return null;
    } catch (error) {
      console.warn('Failed to parse language preference:', error);
      return null;
    }
  }, [LANGUAGE_PREF_KEY]);
  const setLanguagePreference = useCallback(language => {
    if (typeof window === 'undefined' || !window.localStorage || !language?.trim()) return;
    try {
      const preference = {
        language: language.trim(),
        timestamp: Date.now()
      };
      localStorage.setItem(LANGUAGE_PREF_KEY, JSON.stringify(preference));
      if (window.CustomEvent && window.dispatchEvent) {
        const event = new CustomEvent(LANGUAGE_CHANGE_EVENT, {
          detail: preference,
          bubbles: false,
          cancelable: false
        });
        window.dispatchEvent(event);
      }
    } catch (error) {
      console.warn('Failed to save language preference:', error);
    }
  }, [LANGUAGE_PREF_KEY, LANGUAGE_CHANGE_EVENT]);
  const generateTabId = useCallback((title, index) => {
    const cleanTitle = title?.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '') || 'tab';
    return `${baseId}-${cleanTitle}-${index}`;
  }, [baseId]);
  const [announcement, setAnnouncement] = useState('');
  const announceToScreenReader = useCallback((message, duration = 1000) => {
    setAnnouncement(message);
    const timeoutId = setTimeout(() => setAnnouncement(''), duration);
    return () => clearTimeout(timeoutId);
  }, []);
  const processedTabs = useMemo(() => {
    if (!children) return [];
    const tabData = [];
    Children.forEach(children, (child, index) => {
      if (!isValidElement(child) || child.type !== CodeTab) {
        console.warn(`Invalid child at index ${index}. Expected CodeTab component.`);
        return;
      }
      const {title, language, disabled = false} = child.props || ({});
      const displayTitle = title || getLanguageDisplayName(language);
      if (!displayTitle || typeof displayTitle !== 'string') {
        console.warn(`CodeTab at index ${index} missing title and language props`);
        return;
      }
      tabData.push({
        title: displayTitle.trim(),
        language: language?.trim() || null,
        disabled: Boolean(disabled),
        content: child.props.children,
        id: generateTabId(displayTitle, index),
        index
      });
    });
    return tabData;
  }, [children, generateTabId]);
  const [activeIndex, setActiveIndex] = useState(0);
  useEffect(() => {
    if (processedTabs.length === 0) return;
    if (typeof defaultTab === 'number') {
      const clampedIndex = Math.max(0, Math.min(defaultTab, processedTabs.length - 1));
      setActiveIndex(clampedIndex);
      return;
    }
    if (typeof defaultTab === 'string' && defaultTab.trim()) {
      const matchIndex = processedTabs.findIndex(tab => tab.title?.toLowerCase() === defaultTab.toLowerCase().trim());
      if (matchIndex >= 0 && !processedTabs[matchIndex].disabled) {
        setActiveIndex(matchIndex);
        return;
      }
    }
    const firstEnabledIndex = processedTabs.findIndex(tab => !tab.disabled);
    setActiveIndex(firstEnabledIndex >= 0 ? firstEnabledIndex : 0);
  }, [processedTabs, defaultTab]);
  useEffect(() => {
    if (!syncLanguages || processedTabs.length === 0) return;
    const preference = getLanguagePreference();
    if (preference) {
      const matchIndex = findMatchingLanguageTab(processedTabs, preference.language);
      if (matchIndex >= 0) {
        setActiveIndex(matchIndex);
      }
    }
  }, [syncLanguages, processedTabs, getLanguagePreference, findMatchingLanguageTab]);
  const handleTabClick = useCallback(index => {
    const targetTab = processedTabs[index];
    if (!targetTab || targetTab.disabled || index === activeIndex) return;
    const previousIndex = activeIndex;
    setActiveIndex(index);
    const announcement = `${targetTab.title} tab selected. Tab ${index + 1} of ${processedTabs.length}.`;
    const cleanup = announceToScreenReader(announcement);
    if (syncLanguages && targetTab.language) {
      setLanguagePreference(targetTab.language);
    }
    onTabChange?.(index, targetTab, previousIndex);
    return cleanup;
  }, [processedTabs, activeIndex, syncLanguages, setLanguagePreference, onTabChange, announceToScreenReader]);
  useEffect(() => {
    if (!syncLanguages || typeof window === 'undefined') return;
    const handleLanguageChange = event => {
      try {
        const {language} = event.detail || ({});
        if (!language || typeof language !== 'string') return;
        const matchIndex = findMatchingLanguageTab(processedTabs, language);
        if (matchIndex >= 0 && matchIndex !== activeIndex && !processedTabs[matchIndex]?.disabled) {
          setActiveIndex(matchIndex);
          const currentTab = processedTabs[matchIndex];
          const announcement = `Synchronized to ${currentTab.title} tab. Tab ${matchIndex + 1} of ${processedTabs.length}.`;
          announceToScreenReader(announcement, 600);
          onTabChange?.(matchIndex, currentTab, activeIndex);
        }
      } catch (error) {
        console.warn('Error handling language change event:', error);
      }
    };
    window.addEventListener(LANGUAGE_CHANGE_EVENT, handleLanguageChange);
    return () => {
      window.removeEventListener(LANGUAGE_CHANGE_EVENT, handleLanguageChange);
    };
  }, [syncLanguages, processedTabs, activeIndex, findMatchingLanguageTab, LANGUAGE_CHANGE_EVENT, onTabChange, announceToScreenReader]);
  const handleKeyDown = useCallback((event, index) => {
    let targetIndex = -1;
    switch (event.key) {
      case 'ArrowLeft':
        event.preventDefault();
        targetIndex = index > 0 ? index - 1 : processedTabs.length - 1;
        break;
      case 'ArrowRight':
        event.preventDefault();
        targetIndex = index < processedTabs.length - 1 ? index + 1 : 0;
        break;
      case 'Enter':
      case ' ':
        event.preventDefault();
        handleTabClick(index);
        return;
      case 'Home':
        event.preventDefault();
        targetIndex = 0;
        break;
      case 'End':
        event.preventDefault();
        targetIndex = processedTabs.length - 1;
        break;
      default:
        return;
    }
    if (targetIndex !== -1) {
      setActiveIndex(targetIndex);
    }
  }, [processedTabs.length, handleTabClick]);
  if (processedTabs.length === 0) {
    return <div className="p-4 text-gray-500 italic text-center border border-gray-200 rounded-lg">
        No valid tabs found. Please check your CodeTab components.
      </div>;
  }
  const safeActiveIndex = Math.max(0, Math.min(activeIndex, processedTabs.length - 1));
  const activeTab = processedTabs[safeActiveIndex];
  return <div className={`tabs tab-container ${className}`} id={activeTab?.language || 'code-tabs'}>
      {}
      <div aria-live="polite" aria-atomic="true" className="sr-only">
        {announcement}
      </div>

      {}
      <ul role="tablist" aria-label={ariaLabel} className="not-prose mb-6 pb-[1px] flex-none min-w-full overflow-auto border-b border-gray-200 gap-x-6 flex dark:border-gray-200/10" data-component-part="tabs-list">
        {processedTabs.map((tab, index) => {
    const isActive = index === safeActiveIndex;
    const isDisabled = tab.disabled;
    return <li key={tab.id} id={tab.id} className="cursor-pointer">
              <button role="tab" aria-selected={isActive} aria-controls={`${tab.id}-panel`} aria-disabled={isDisabled} aria-setsize={processedTabs.length} aria-posinset={index + 1} tabIndex={isActive ? 0 : -1} disabled={isDisabled} onClick={() => handleTabClick(index)} onKeyDown={e => handleKeyDown(e, index)} className={isDisabled ? 'flex text-sm items-center gap-1.5 leading-6 font-semibold whitespace-nowrap pt-3 pb-2.5 -mb-px max-w-max border-b text-gray-400 border-transparent cursor-not-allowed opacity-50' : isActive ? 'flex text-sm items-center gap-1.5 leading-6 font-semibold whitespace-nowrap pt-3 pb-2.5 -mb-px max-w-max border-b text-primary dark:text-primary-light border-current' : 'flex text-sm items-center gap-1.5 leading-6 font-semibold whitespace-nowrap pt-3 pb-2.5 -mb-px max-w-max border-b text-gray-900 border-transparent hover:border-gray-300 dark:text-gray-200 dark:hover:border-gray-700'} data-component-part="tab-button" data-active={isActive}>
                {tab.title}
              </button>
            </li>;
  })}
      </ul>

      {}
      <div role="tabpanel" id={`${activeTab?.id}-panel`} aria-labelledby={activeTab?.id} aria-label={`${activeTab?.title} content, tab panel ${safeActiveIndex + 1} of ${processedTabs.length}`} tabIndex={0} className="prose dark:prose-dark overflow-x-auto" data-component-part="tab-content">
        {activeTab?.content}
      </div>
    </div>;
};

Local authorization allows you to generate SQL queries that can be executed directly against your database, providing better performance for authorization checks by avoiding round trips to Oso Cloud.

## List Local

Generate SQL conditions to filter resources that an actor can access, perfect for implementing authorization in database queries.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Generate SQL condition for authorized resources
    const sqlCondition = await oso.listLocal(alice, "read", "Issue", "id");

    // Use with database query (example with Kysely)
    const authorized_issues = await db
      .selectFrom("issues")
      .where(sql.raw<boolean>(sqlCondition))
      .selectAll()
      .execute();

    // With context facts
    const sqlCondition = await oso.listLocal(
      alice, 
      "read", 
      "Issue", 
      "id",
      [["has_role", alice, "admin", repository]]  // Context facts
    );

    console.log("Authorized issues:", authorized_issues.length);
    ```

    **Signature:** `oso.listLocal(actor, action, resource_type, column, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    # Generate SQL condition for authorized resources
    sql_condition = oso.list_local(alice, "read", "Issue", "id")

    # Use with SQLAlchemy
    from sqlalchemy import select, text

    authorized_issues = session.scalars(
        select(Issues).filter(text(sql_condition))
    ).all()

    # With context facts
    sql_condition = oso.list_local(
        alice, 
        "read", 
        "Issue", 
        "id",
        context_facts=[("has_role", alice, "admin", repository)]
    )

    print(f"Found {len(authorized_issues)} authorized issues")
    ```

    **Signature:** `oso.list_local(actor, action, resource_type, column, context_facts=None)`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // Generate SQL condition for authorized resources
    sqlCondition, err := osoClient.ListLocal(user, "read", "Issue", "id")
    if err != nil {
        log.Fatal(err)
    }

    // Use with GORM
    var issues []Issue
    db.Find(&issues, sqlCondition)

    // With context facts
    contextFacts := []oso.Fact{
        oso.NewFact("has_role", user, oso.String("admin"), repository),
    }
    sqlCondition, err = osoClient.ListLocalWithContext(user, "read", "Issue", "id", contextFacts)
    if err != nil {
        log.Fatal(err)
    }

    var contextIssues []Issue
    db.Find(&contextIssues, sqlCondition)

    fmt.Printf("Found %d authorized issues\n", len(issues))
    ```

    **Signature:** `osoClient.ListLocal(actor, action, resourceType, column)` | `osoClient.ListLocalWithContext(actor, action, resourceType, column, contextFacts)`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // Generate SQL condition for authorized resources
    String filterSql = oso.listLocal(alice, "read", "Issue", "id");

    // Use with JPA
    List<Issue> authorizedIssues = entityManager
        .createNativeQuery("SELECT * FROM issues WHERE " + filterSql, Issue.class)
        .getResultList();

    // With context facts
    List<Fact> contextFacts = Arrays.asList(
        new Fact("has_role", Arrays.asList(alice, new Value("admin"), repository))
    );
    String contextFilterSql = oso.listLocal(alice, "read", "Issue", "id", contextFacts);

    List<Issue> contextAuthorizedIssues = entityManager
        .createNativeQuery("SELECT * FROM issues WHERE " + contextFilterSql, Issue.class)
        .getResultList();

    System.out.println("Found " + authorizedIssues.size() + " authorized issues");
    ```

    **Signature:** `oso.listLocal(actor, action, resourceType, column, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # Generate SQL condition for authorized resources
    sql_condition = oso.list_local(alice, "read", "Issue", "id")

    # Use with ActiveRecord
    authorized_issues = Issue.where(sql_condition)

    # With context facts
    sql_condition_with_context = oso.list_local(
      bob, 
      "read", 
      "Issue", 
      "id", 
      context_facts: [["has_role", bob, "admin", anvils_repository]]
    )

    context_authorized_issues = Issue.where(sql_condition_with_context)

    puts "Found #{authorized_issues.count} authorized issues"
    ```

    **Signature:** `oso.list_local(actor, action, resource_type, column, context_facts=nil)`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // Generate SQL condition for authorized resources
    var filterResult = await oso.ListLocal(alice, "read", "Issue", "id");
    var filterSql = filterResult.Sql;

    // Use with Entity Framework
    var authorizedIssues = db.Issues
        .FromSqlRaw($"SELECT * FROM issue WHERE {filterSql}")
        .ToList();

    // With context facts
    var contextFacts = new List<Fact> { 
        new Fact("has_role", new List<Value> { alice, new Value("String", "admin"), repository })
    };
    var contextFilterResult = await oso.ListLocal(alice, "read", "Issue", "id", contextFacts);
    var contextFilterSql = contextFilterResult.Sql;

    Console.WriteLine($"Found {authorizedIssues.Count} authorized issues");
    ```

    **Signature:** `oso.ListLocal(actor, action, resource_type, column, contextFacts?)`
  </CodeTab>

  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # Generate SQL condition for local authorization
    oso-cloud list-local \
      --data-bindings path/to/local_authorization_config.yaml \
      User:alice read Issue id

    # This outputs SQL that can be used in your database queries
    ```

    **Signature:** `oso-cloud list-local --data-bindings <config-file> <actor> <action> <resource-type> <column>`
  </CodeTab>
</CodeTabs>

## Authorize Local

Generate SQL queries to check if an actor is authorized to perform an action on a specific resource.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Generate authorization check SQL
    const query = await oso.authorizeLocal(alice, "read", swage_issue);

    // Execute with database (example with raw SQL)
    const result = await sql.raw<AuthorizeResult>(query).execute(db);
    const { allowed } = result.rows[0];

    if (!allowed) {
      throw new Error("Access denied");
    }

    // With context facts
    const contextQuery = await oso.authorizeLocal(
      alice, 
      "read", 
      swage_issue,
      [["has_role", alice, "admin", repository]]
    );

    console.log(`Access ${allowed ? 'granted' : 'denied'}`);
    ```

    **Signature:** `oso.authorizeLocal(actor, action, resource, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    # Generate authorization check SQL
    query = oso.authorize_local(alice, "read", swage_issue)

    # Execute with SQLAlchemy
    from sqlalchemy import text

    authorized = session.execute(text(query)).scalar()

    if not authorized:
        raise Exception("Access denied")

    # With context facts
    context_query = oso.authorize_local(
        alice, 
        "read", 
        swage_issue,
        context_facts=[("has_role", alice, "admin", repository)]
    )

    context_authorized = session.execute(text(context_query)).scalar()
    print(f"Access {'granted' if authorized else 'denied'}")
    ```

    **Signature:** `oso.authorize_local(actor, action, resource, context_facts=None)`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // Generate authorization check SQL
    query, err := osoClient.AuthorizeLocal(alice, "read", swageIssue)
    if err != nil {
        log.Fatal(err)
    }

    // Execute with GORM
    var authorizeResult AuthorizeResult
    db.Raw(query).Scan(&authorizeResult)

    if !authorizeResult.Allowed {
        return fmt.Errorf("access denied")
    }

    // With context facts
    contextFacts := []oso.Fact{
        oso.NewFact("has_role", alice, oso.String("admin"), repository),
    }
    contextQuery, err := osoClient.AuthorizeLocalWithContext(alice, "read", swageIssue, contextFacts)
    if err != nil {
        log.Fatal(err)
    }

    var contextAuthorizeResult AuthorizeResult
    db.Raw(contextQuery).Scan(&contextAuthorizeResult)

    fmt.Printf("Access %s\n", map[bool]string{true: "granted", false: "denied"}[authorizeResult.Allowed])
    ```

    **Signature:** `osoClient.AuthorizeLocal(actor, action, resource)` | `osoClient.AuthorizeLocalWithContext(actor, action, resource, contextFacts)`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // Generate authorization check SQL
    String sqlQuery = oso.authorizeLocal(alice, "read", swageIssue);

    // Execute with JPA
    boolean allowed = (Boolean) entityManager
        .createNativeQuery(sqlQuery)
        .getSingleResult();

    if (!allowed) {
        throw new SecurityException("Access denied");
    }

    // With context facts
    List<Fact> contextFacts = Arrays.asList(
        new Fact("has_role", Arrays.asList(alice, new Value("admin"), repository))
    );
    String contextSqlQuery = oso.authorizeLocal(alice, "read", swageIssue, contextFacts);

    boolean contextAllowed = (Boolean) entityManager
        .createNativeQuery(contextSqlQuery)
        .getSingleResult();

    System.out.println("Access " + (allowed ? "granted" : "denied"));
    ```

    **Signature:** `oso.authorizeLocal(actor, action, resource, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # Generate authorization check SQL
    query = oso.authorize_local(alice, "read", swage_issue)

    # Execute with ActiveRecord
    allowed = Issue.connection.select_value(query)

    raise "Access denied" unless allowed

    # With context facts
    context_query = oso.authorize_local(
      bob, 
      "read", 
      swage_issue, 
      context_facts: [["has_role", bob, "admin", anvils_repository]]
    )

    context_allowed = Issue.connection.select_value(context_query)
    puts allowed ? "Access granted" : "Access denied"
    ```

    **Signature:** `oso.authorize_local(actor, action, resource, context_facts=nil)`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // Generate authorization check SQL
    var sqlResult = await oso.AuthorizeLocal(alice, "read", swage_issue);
    var sql = sqlResult.Sql;

    // Execute with Entity Framework
    var result = db.AuthorizeResults
        .FromSqlRaw(sql)
        .Single();

    if (!result.Allowed) {
        throw new UnauthorizedAccessException("Access denied");
    }

    // With context facts
    var contextFacts = new List<Fact> { 
        new Fact("has_role", new List<Value> { alice, new Value("String", "admin"), repository })
    };
    var contextSqlResult = await oso.AuthorizeLocal(alice, "read", swage_issue, contextFacts);
    var contextSql = contextSqlResult.Sql;

    Console.WriteLine($"Access {(result.Allowed ? "granted" : "denied")}");
    ```

    **Signature:** `oso.AuthorizeLocal(actor, action, resource, contextFacts?)`
  </CodeTab>

  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # Generate authorization check SQL
    oso-cloud authorize-local \
      --data-bindings path/to/local_authorization_config.yaml \
      User:alice read Issue:swage

    # This outputs SQL that returns a boolean result
    ```

    **Signature:** `oso-cloud authorize-local --data-bindings <config-file> <actor> <action> <resource>`
  </CodeTab>
</CodeTabs>

## Actions Local

Generate SQL queries to get all actions an actor can perform on a specific resource.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Generate actions query SQL
    const query = await oso.actionsLocal(alice, swage_issue);

    // Execute with database
    const result = await sql.raw<{ actions: string }>(query).execute(db);
    const actions = result.rows.map(row => row.actions);

    console.log("Available actions:", actions);

    // With context facts
    const contextQuery = await oso.actionsLocal(
      alice, 
      swage_issue,
      [["has_role", alice, "admin", repository]]
    );

    const contextResult = await sql.raw<{ actions: string }>(contextQuery).execute(db);
    const contextActions = contextResult.rows.map(row => row.actions);
    ```

    **Signature:** `oso.actionsLocal(actor, resource, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    # Generate actions query SQL
    query = oso.actions_local(alice, swage_issue)

    # Execute with SQLAlchemy
    from sqlalchemy import text

    actions = list(session.execute(text(query)).scalars())

    print(f"Available actions: {actions}")

    # With context facts
    context_query = oso.actions_local(
        alice, 
        swage_issue,
        context_facts=[("has_role", alice, "admin", repository)]
    )

    context_actions = list(session.execute(text(context_query)).scalars())
    ```

    **Signature:** `oso.actions_local(actor, resource, context_facts=None)`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // Generate actions query SQL
    query, err := osoClient.ActionsLocal(alice, swageIssue)
    if err != nil {
        log.Fatal(err)
    }

    // Execute with GORM
    var actions []string
    db.Raw(query).Pluck("actions", &actions)

    fmt.Printf("Available actions: %v\n", actions)

    // With context facts
    contextFacts := []oso.Fact{
        oso.NewFact("has_role", alice, oso.String("admin"), repository),
    }
    contextQuery, err := osoClient.ActionsLocalWithContext(alice, swageIssue, contextFacts)
    if err != nil {
        log.Fatal(err)
    }

    var contextActions []string
    db.Raw(contextQuery).Pluck("actions", &contextActions)
    ```

    **Signature:** `osoClient.ActionsLocal(actor, resource)` | `osoClient.ActionsLocalWithContext(actor, resource, contextFacts)`
  </CodeTab>

  <CodeTab language={Languages.JAVA}>
    ```java theme={null}
    // Generate actions query SQL
    String sqlQuery = oso.actionsLocal(alice, swageIssue);

    // Execute with JPA
    @SuppressWarnings("unchecked")
    List<String> actions = entityManager
        .createNativeQuery(sqlQuery)
        .getResultList();

    System.out.println("Available actions: " + actions);

    // With context facts
    List<Fact> contextFacts = Arrays.asList(
        new Fact("has_role", Arrays.asList(alice, new Value("admin"), repository))
    );
    String contextSqlQuery = oso.actionsLocal(alice, swageIssue, contextFacts);

    @SuppressWarnings("unchecked")
    List<String> contextActions = entityManager
        .createNativeQuery(contextSqlQuery)
        .getResultList();
    ```

    **Signature:** `oso.actionsLocal(actor, resource, contextFacts?)`
  </CodeTab>

  <CodeTab language={Languages.RUBY}>
    ```ruby theme={null}
    # Generate actions query SQL
    query = oso.actions_local(alice, swage_issue)

    # Execute with ActiveRecord
    actions = Issue.connection.select_all(query).pluck("actions")

    puts "Available actions: #{actions}"

    # With context facts
    context_query = oso.actions_local(
      bob, 
      swage_issue, 
      context_facts: [["has_role", bob, "admin", anvils_repository]]
    )

    context_actions = Issue.connection.select_all(context_query).pluck("actions")
    ```

    **Signature:** `oso.actions_local(actor, resource, context_facts=nil)`
  </CodeTab>

  <CodeTab language={Languages.DOTNET}>
    ```csharp theme={null}
    // Generate actions query SQL
    var sqlResult = await oso.ActionsLocal(bob, environmentAny);
    var sql = sqlResult.Sql;

    // Execute with Entity Framework
    var result = db.ActionResults
        .FromSqlRaw(sql)
        .ToList();

    var actions = result.Select(a => a.Actions).ToList();
    Console.WriteLine($"Available actions: {string.Join(", ", actions)}");

    // With context facts
    var contextFacts = new List<Fact> { 
        new Fact("has_role", new List<Value> { bob, new Value("String", "admin"), repository })
    };
    var contextSqlResult = await oso.ActionsLocal(bob, environmentAny, contextFacts);
    var contextSql = contextSqlResult.Sql;
    ```

    **Signature:** `oso.ActionsLocal(actor, resource, contextFacts?)`
  </CodeTab>
</CodeTabs>

## Query Builder Local

Use the query builder to generate SQL for complex authorization queries with additional constraints.

<CodeTabs>
  <CodeTab title="Node.js" language={Languages.JAVASCRIPT}>
    ```javascript theme={null}
    // Generate SQL for field-level authorization
    const sqlQuery = await oso.buildQuery([
      "allow_field", 
      actor, 
      "read", 
      resource, 
      field
    ]).evaluateLocalSelect({ field_name: field });

    // Execute field authorization query
    const fieldResult = await sql.raw(sqlQuery).execute(db);

    // Generate SQL filter for authorized users
    const authorizedUserFragment = await oso.buildQuery([
      "allow", 
      actor_var, 
      "read", 
      resource
    ]).evaluateLocalFilter("id", actor_var);

    // Use in WHERE clause
    const filteredQuery = `SELECT * FROM users WHERE ${authorizedUserFragment}`;
    ```

    **Methods:** `evaluateLocalSelect({field_name: field})` | `evaluateLocalFilter("column", variable)`
  </CodeTab>

  <CodeTab language={Languages.PYTHON}>
    ```python theme={null}
    # Generate SQL for field-level authorization
    sql_query = oso.build_query((
      "allow_field", 
      actor, 
      "read", 
      resource, 
      field
    )).evaluate_local_select({"field_name": field})

    # Execute field authorization query
    field_result = session.execute(text(sql_query)).fetchall()

    # Generate SQL filter for authorized users
    authorized_user_fragment = oso.build_query((
      "allow", 
      actor_var, 
      "read", 
      resource
    )).evaluate_local_filter("id", actor_var)

    # Use in WHERE clause
    filtered_query = f"SELECT * FROM users WHERE {authorized_user_fragment}"
    ```

    **Methods:** `evaluate_local_select({"field_name": field})` | `evaluate_local_filter("column", variable)`
  </CodeTab>

  <CodeTab language={Languages.GO}>
    ```go theme={null}
    // Generate SQL for field-level authorization
    sqlQuery, err := osoClient.BuildQuery(
      oso.NewQueryFact("allow_field", actor, oso.String("read"), resource, fieldVar),
    ).EvaluateLocalSelect(map[string]oso.Variable{"field_name": fieldVar})
    if err != nil {
        log.Fatal(err)
    }

    // Execute field authorization query
    var fieldResult []map[string]interface{}
    db.Raw(sqlQuery).Scan(&fieldResult)

    // Generate SQL filter for authorized users
    authorizedUserFragment, err := osoClient.BuildQuery(
      oso.NewQueryFact("allow", actorVar, oso.String("read"), resource),
    ).EvaluateLocalFilter("id", actorVar)
    if err != nil {
        log.Fatal(err)
    }

    // Use in WHERE clause
    filteredQuery := fmt.Sprintf("SELECT * FROM users WHERE %s", authorizedUserFragment)
    ```

    **Methods:** `EvaluateLocalSelect(map[string]Variable{"field_name": fieldVar})` | `EvaluateLocalFilter("column", variable)`
  </CodeTab>
</CodeTabs>

## Configuration Validation

Validate your local authorization configuration before using it in production.

<CodeTabs>
  <CodeTab title="CLI" language={Languages.BASH}>
    ```bash theme={null}
    # Validate local authorization configuration
    oso-cloud validate-local-authorization-config \
      path/to/local_authorization_config.yaml \
      -c postgres://localhost:5432/mydb \
      -p main.polar

    # This checks that your configuration is valid and can connect to the database
    ```

    **Signature:** `oso-cloud validate-local-authorization-config <config-file> -c <connection-string> -p <policy-file>`
  </CodeTab>
</CodeTabs>
