Skip to content

Permissions API

Types

PermissionAction

pydantic_ai_backends.permissions.types.PermissionAction = Literal['allow', 'deny', 'ask'] module-attribute

PermissionOperation

pydantic_ai_backends.permissions.types.PermissionOperation = Literal['read', 'write', 'edit', 'execute', 'glob', 'grep', 'ls'] module-attribute

PermissionRule

pydantic_ai_backends.permissions.types.PermissionRule

Bases: BaseModel

A rule that matches paths/commands and specifies an action.

Rules are evaluated in order - first matching rule wins. Patterns use fnmatch-style matching with support for ** (recursive).

Example
Python
# Deny access to .env files
PermissionRule(
    pattern="**/.env*",
    action="deny",
    description="Protect environment files",
)

# Ask before writing to config files
PermissionRule(
    pattern="**/config/**",
    action="ask",
    description="Confirm config changes",
)
Source code in src/pydantic_ai_backends/permissions/types.py
Python
class PermissionRule(BaseModel):
    """A rule that matches paths/commands and specifies an action.

    Rules are evaluated in order - first matching rule wins.
    Patterns use fnmatch-style matching with support for `**` (recursive).

    Example:
        ```python
        # Deny access to .env files
        PermissionRule(
            pattern="**/.env*",
            action="deny",
            description="Protect environment files",
        )

        # Ask before writing to config files
        PermissionRule(
            pattern="**/config/**",
            action="ask",
            description="Confirm config changes",
        )
        ```
    """

    pattern: str
    """Glob pattern to match against paths or commands.

    Supports fnmatch patterns:
    - `*` matches any characters except `/`
    - `**` matches any characters including `/` (recursive)
    - `?` matches any single character
    - `[seq]` matches any character in seq
    """

    action: PermissionAction
    """Action to take when pattern matches: "allow", "deny", or "ask"."""

    description: str = ""
    """Human-readable description of why this rule exists."""

pattern instance-attribute

Glob pattern to match against paths or commands.

Supports fnmatch patterns: - * matches any characters except / - ** matches any characters including / (recursive) - ? matches any single character - [seq] matches any character in seq

action instance-attribute

Action to take when pattern matches: "allow", "deny", or "ask".

description = '' class-attribute instance-attribute

Human-readable description of why this rule exists.

OperationPermissions

pydantic_ai_backends.permissions.types.OperationPermissions

Bases: BaseModel

Permissions configuration for a single operation type.

Contains a default action and a list of rules that override the default for specific patterns.

Example
Python
OperationPermissions(
    default="allow",
    rules=[
        PermissionRule(pattern="**/.env*", action="deny"),
        PermissionRule(pattern="**/secrets/**", action="deny"),
    ],
)
Source code in src/pydantic_ai_backends/permissions/types.py
Python
class OperationPermissions(BaseModel):
    """Permissions configuration for a single operation type.

    Contains a default action and a list of rules that override
    the default for specific patterns.

    Example:
        ```python
        OperationPermissions(
            default="allow",
            rules=[
                PermissionRule(pattern="**/.env*", action="deny"),
                PermissionRule(pattern="**/secrets/**", action="deny"),
            ],
        )
        ```
    """

    default: PermissionAction = "allow"
    """Default action when no rule matches."""

    rules: list[PermissionRule] = Field(default_factory=list)
    """Rules evaluated in order - first match wins."""

default = 'allow' class-attribute instance-attribute

Default action when no rule matches.

rules = Field(default_factory=list) class-attribute instance-attribute

Rules evaluated in order - first match wins.

PermissionRuleset

pydantic_ai_backends.permissions.types.PermissionRuleset

Bases: BaseModel

Complete permissions configuration for all operations.

Defines default behavior and per-operation permissions. Each operation can have its own default and rules.

Example
Python
ruleset = PermissionRuleset(
    default="deny",  # Default deny everything
    read=OperationPermissions(default="allow"),  # But allow reads
    write=OperationPermissions(
        default="ask",  # Ask before writing
        rules=[
            PermissionRule(pattern="**/temp/**", action="allow"),
        ],
    ),
)
Source code in src/pydantic_ai_backends/permissions/types.py
Python
class PermissionRuleset(BaseModel):
    """Complete permissions configuration for all operations.

    Defines default behavior and per-operation permissions.
    Each operation can have its own default and rules.

    Example:
        ```python
        ruleset = PermissionRuleset(
            default="deny",  # Default deny everything
            read=OperationPermissions(default="allow"),  # But allow reads
            write=OperationPermissions(
                default="ask",  # Ask before writing
                rules=[
                    PermissionRule(pattern="**/temp/**", action="allow"),
                ],
            ),
        )
        ```
    """

    default: PermissionAction = "ask"
    """Global default action when operation has no specific config."""

    read: OperationPermissions | None = None
    """Permissions for read operations."""

    write: OperationPermissions | None = None
    """Permissions for write operations."""

    edit: OperationPermissions | None = None
    """Permissions for edit operations."""

    execute: OperationPermissions | None = None
    """Permissions for execute operations (shell commands)."""

    glob: OperationPermissions | None = None
    """Permissions for glob operations."""

    grep: OperationPermissions | None = None
    """Permissions for grep operations."""

    ls: OperationPermissions | None = None
    """Permissions for ls operations."""

    def get_operation_permissions(self, operation: PermissionOperation) -> OperationPermissions:
        """Get permissions for a specific operation.

        Returns the operation-specific permissions if defined,
        otherwise creates default permissions using the global default.

        Args:
            operation: The operation type to get permissions for.

        Returns:
            OperationPermissions for the specified operation.
        """
        op_perms: OperationPermissions | None = getattr(self, operation, None)
        if op_perms is not None:
            return op_perms
        return OperationPermissions(default=self.default)

default = 'ask' class-attribute instance-attribute

Global default action when operation has no specific config.

read = None class-attribute instance-attribute

Permissions for read operations.

write = None class-attribute instance-attribute

Permissions for write operations.

edit = None class-attribute instance-attribute

Permissions for edit operations.

execute = None class-attribute instance-attribute

Permissions for execute operations (shell commands).

glob = None class-attribute instance-attribute

Permissions for glob operations.

grep = None class-attribute instance-attribute

Permissions for grep operations.

ls = None class-attribute instance-attribute

Permissions for ls operations.

get_operation_permissions(operation)

Get permissions for a specific operation.

Returns the operation-specific permissions if defined, otherwise creates default permissions using the global default.

Parameters:

Name Type Description Default
operation PermissionOperation

The operation type to get permissions for.

required

Returns:

Type Description
OperationPermissions

OperationPermissions for the specified operation.

Source code in src/pydantic_ai_backends/permissions/types.py
Python
def get_operation_permissions(self, operation: PermissionOperation) -> OperationPermissions:
    """Get permissions for a specific operation.

    Returns the operation-specific permissions if defined,
    otherwise creates default permissions using the global default.

    Args:
        operation: The operation type to get permissions for.

    Returns:
        OperationPermissions for the specified operation.
    """
    op_perms: OperationPermissions | None = getattr(self, operation, None)
    if op_perms is not None:
        return op_perms
    return OperationPermissions(default=self.default)

Checker

PermissionChecker

pydantic_ai_backends.permissions.checker.PermissionChecker

Checks operations against a permission ruleset.

Rules are evaluated in order and the first match wins. With no match the operation's default applies, falling back to the ruleset's global default.

Example
Python
from pydantic_ai_backends.permissions import DEFAULT_RULESET, PermissionChecker

async def ask_user(op: str, target: str, reason: str) -> bool:
    return input(f"Allow {op} on {target}? ").lower() == "y"

checker = PermissionChecker(ruleset=DEFAULT_RULESET, ask_callback=ask_user)

action = checker.check_sync("read", "/path/to/file")
allowed = await checker.check("write", "/path/to/file", "Save changes")
Source code in src/pydantic_ai_backends/permissions/checker.py
Python
class PermissionChecker:
    """Checks operations against a permission ruleset.

    Rules are evaluated in order and the first match wins. With no match the
    operation's default applies, falling back to the ruleset's global default.

    Example:
        ```python
        from pydantic_ai_backends.permissions import DEFAULT_RULESET, PermissionChecker

        async def ask_user(op: str, target: str, reason: str) -> bool:
            return input(f"Allow {op} on {target}? ").lower() == "y"

        checker = PermissionChecker(ruleset=DEFAULT_RULESET, ask_callback=ask_user)

        action = checker.check_sync("read", "/path/to/file")
        allowed = await checker.check("write", "/path/to/file", "Save changes")
        ```
    """

    def __init__(
        self,
        ruleset: PermissionRuleset,
        ask_callback: AskCallback | None = None,
        ask_fallback: AskFallback = "error",
    ):
        """Initialize the checker.

        Args:
            ruleset: The ruleset to check against.
            ask_callback: Async callback for "ask" actions.
            ask_fallback: What an unanswerable "ask" does — `"deny"` returns
                False, `"error"` raises.
        """
        self._ruleset = ruleset
        self._ask_callback = ask_callback
        self._ask_fallback = ask_fallback

    @property
    def ruleset(self) -> PermissionRuleset:
        """The ruleset being checked against."""
        return self._ruleset

    def check_sync(self, operation: PermissionOperation, target: str) -> PermissionAction:
        """Resolve the action for an operation without invoking any callback.

        Args:
            operation: The operation type.
            target: The path or command being accessed.
        """
        rule = self.find_matching_rule(operation, target)
        if rule is not None:
            return rule.action
        return self._ruleset.get_operation_permissions(operation).default

    def find_matching_rule(
        self, operation: PermissionOperation, target: str
    ) -> PermissionRule | None:
        """The first rule matching this operation and target, if any."""
        permissions = self._ruleset.get_operation_permissions(operation)
        return next(
            (rule for rule in permissions.rules if matches_pattern(target, rule.pattern)),
            None,
        )

    async def check(
        self,
        operation: PermissionOperation,
        target: str,
        reason: str = "",
    ) -> bool:
        """Resolve an operation, asking for approval when the rules say so.

        Args:
            operation: The operation type.
            target: The path or command being accessed.
            reason: Human-readable reason, passed to the callback.

        Returns:
            True when the operation is allowed.

        Raises:
            PermissionDeniedError: If it is denied, or approval was refused.
            PermissionAskError: If approval is needed, no callback can give it
                and `ask_fallback="error"`.
        """
        action = self.check_sync(operation, target)

        if action == "allow":
            return True

        if action == "deny":
            raise PermissionDeniedError(
                operation, target, self.find_matching_rule(operation, target)
            )

        if self._ask_callback is not None:
            if await self._ask_callback(operation, target, reason):
                return True
            raise PermissionDeniedError(operation, target)

        if self._ask_fallback == "error":
            raise PermissionAskError(operation, target, reason)
        raise PermissionDeniedError(operation, target)

    def is_allowed(self, operation: PermissionOperation, target: str) -> bool:
        """Whether the operation would proceed without asking."""
        return self.check_sync(operation, target) == "allow"

    def is_denied(self, operation: PermissionOperation, target: str) -> bool:
        """Whether the operation would be refused outright."""
        return self.check_sync(operation, target) == "deny"

    def requires_approval(self, operation: PermissionOperation, target: str) -> bool:
        """Whether the operation would need user approval."""
        return self.check_sync(operation, target) == "ask"

ruleset property

The ruleset being checked against.

__init__(ruleset, ask_callback=None, ask_fallback='error')

Initialize the checker.

Parameters:

Name Type Description Default
ruleset PermissionRuleset

The ruleset to check against.

required
ask_callback AskCallback | None

Async callback for "ask" actions.

None
ask_fallback AskFallback

What an unanswerable "ask" does — "deny" returns False, "error" raises.

'error'
Source code in src/pydantic_ai_backends/permissions/checker.py
Python
def __init__(
    self,
    ruleset: PermissionRuleset,
    ask_callback: AskCallback | None = None,
    ask_fallback: AskFallback = "error",
):
    """Initialize the checker.

    Args:
        ruleset: The ruleset to check against.
        ask_callback: Async callback for "ask" actions.
        ask_fallback: What an unanswerable "ask" does — `"deny"` returns
            False, `"error"` raises.
    """
    self._ruleset = ruleset
    self._ask_callback = ask_callback
    self._ask_fallback = ask_fallback

check_sync(operation, target)

Resolve the action for an operation without invoking any callback.

Parameters:

Name Type Description Default
operation PermissionOperation

The operation type.

required
target str

The path or command being accessed.

required
Source code in src/pydantic_ai_backends/permissions/checker.py
Python
def check_sync(self, operation: PermissionOperation, target: str) -> PermissionAction:
    """Resolve the action for an operation without invoking any callback.

    Args:
        operation: The operation type.
        target: The path or command being accessed.
    """
    rule = self.find_matching_rule(operation, target)
    if rule is not None:
        return rule.action
    return self._ruleset.get_operation_permissions(operation).default

check(operation, target, reason='') async

Resolve an operation, asking for approval when the rules say so.

Parameters:

Name Type Description Default
operation PermissionOperation

The operation type.

required
target str

The path or command being accessed.

required
reason str

Human-readable reason, passed to the callback.

''

Returns:

Type Description
bool

True when the operation is allowed.

Raises:

Type Description
PermissionDeniedError

If it is denied, or approval was refused.

PermissionAskError

If approval is needed, no callback can give it and ask_fallback="error".

Source code in src/pydantic_ai_backends/permissions/checker.py
Python
async def check(
    self,
    operation: PermissionOperation,
    target: str,
    reason: str = "",
) -> bool:
    """Resolve an operation, asking for approval when the rules say so.

    Args:
        operation: The operation type.
        target: The path or command being accessed.
        reason: Human-readable reason, passed to the callback.

    Returns:
        True when the operation is allowed.

    Raises:
        PermissionDeniedError: If it is denied, or approval was refused.
        PermissionAskError: If approval is needed, no callback can give it
            and `ask_fallback="error"`.
    """
    action = self.check_sync(operation, target)

    if action == "allow":
        return True

    if action == "deny":
        raise PermissionDeniedError(
            operation, target, self.find_matching_rule(operation, target)
        )

    if self._ask_callback is not None:
        if await self._ask_callback(operation, target, reason):
            return True
        raise PermissionDeniedError(operation, target)

    if self._ask_fallback == "error":
        raise PermissionAskError(operation, target, reason)
    raise PermissionDeniedError(operation, target)

is_allowed(operation, target)

Whether the operation would proceed without asking.

Source code in src/pydantic_ai_backends/permissions/checker.py
Python
def is_allowed(self, operation: PermissionOperation, target: str) -> bool:
    """Whether the operation would proceed without asking."""
    return self.check_sync(operation, target) == "allow"

is_denied(operation, target)

Whether the operation would be refused outright.

Source code in src/pydantic_ai_backends/permissions/checker.py
Python
def is_denied(self, operation: PermissionOperation, target: str) -> bool:
    """Whether the operation would be refused outright."""
    return self.check_sync(operation, target) == "deny"

requires_approval(operation, target)

Whether the operation would need user approval.

Source code in src/pydantic_ai_backends/permissions/checker.py
Python
def requires_approval(self, operation: PermissionOperation, target: str) -> bool:
    """Whether the operation would need user approval."""
    return self.check_sync(operation, target) == "ask"

PermissionAskError

Raised when a permission check resolves to "ask" but no ask_callback is available and ask_fallback="error".

pydantic_ai_backends.permissions.checker.PermissionAskError

Bases: Exception

Raised when an operation needs approval and ask_fallback="error".

Named PermissionAskError so it does not shadow the builtin PermissionError (an OSError subclass) for importers of this module.

Attributes:

Name Type Description
operation

The operation that needed approval.

target

The path or command it addressed.

reason

Why approval was being sought.

Source code in src/pydantic_ai_backends/permissions/checker.py
Python
class PermissionAskError(Exception):
    """Raised when an operation needs approval and `ask_fallback="error"`.

    Named `PermissionAskError` so it does not shadow the builtin
    `PermissionError` (an `OSError` subclass) for importers of this module.

    Attributes:
        operation: The operation that needed approval.
        target: The path or command it addressed.
        reason: Why approval was being sought.
    """

    def __init__(
        self,
        operation: PermissionOperation,
        target: str,
        reason: str = "",
    ):
        self.operation = operation
        self.target = target
        self.reason = reason
        message = f"Permission required for {operation} on '{target}'"
        if reason:
            message += f": {reason}"
        super().__init__(message)

PermissionError

Deprecated

PermissionError is a deprecated alias for PermissionAskError. It shadows the builtin PermissionError; use PermissionAskError instead.

pydantic_ai_backends.permissions.checker.PermissionError

Bases: PermissionAskError

Deprecated alias for :class:PermissionAskError.

Source code in src/pydantic_ai_backends/permissions/checker.py
Python
@deprecated("Use `PermissionAskError` instead; this name shadows the builtin PermissionError.")
class PermissionError(PermissionAskError):
    """Deprecated alias for :class:`PermissionAskError`."""

PermissionDeniedError

pydantic_ai_backends.permissions.checker.PermissionDeniedError

Bases: Exception

Raised when an operation is explicitly denied.

Attributes:

Name Type Description
operation

The operation that was denied.

target

The path or command it addressed.

rule

The rule that denied it, when a rule rather than a default did.

Source code in src/pydantic_ai_backends/permissions/checker.py
Python
class PermissionDeniedError(Exception):
    """Raised when an operation is explicitly denied.

    Attributes:
        operation: The operation that was denied.
        target: The path or command it addressed.
        rule: The rule that denied it, when a rule rather than a default did.
    """

    def __init__(
        self,
        operation: PermissionOperation,
        target: str,
        rule: PermissionRule | None = None,
    ):
        self.operation = operation
        self.target = target
        self.rule = rule
        message = f"Permission denied for {operation} on '{target}'"
        if rule and rule.description:
            message += f": {rule.description}"
        super().__init__(message)

Presets

DEFAULT_RULESET

pydantic_ai_backends.permissions.presets.DEFAULT_RULESET = PermissionRuleset(default='ask', read=OperationPermissions(default='allow', rules=deny_rules(SECRETS_PATTERNS, SECRETS_DESCRIPTION)), write=OperationPermissions(default='ask', rules=deny_rules(SECRETS_PATTERNS, SECRETS_DESCRIPTION)), edit=OperationPermissions(default='ask', rules=deny_rules(SECRETS_PATTERNS, SECRETS_DESCRIPTION)), execute=OperationPermissions(default='ask', rules=deny_rules(DANGEROUS_COMMANDS, DANGEROUS_DESCRIPTION)), glob=OperationPermissions(default='allow'), grep=OperationPermissions(default='allow'), ls=OperationPermissions(default='allow')) module-attribute

Safe default: reads allowed except secrets, writes and commands ask first.

PERMISSIVE_RULESET

pydantic_ai_backends.permissions.presets.PERMISSIVE_RULESET = PermissionRuleset(default='allow', read=OperationPermissions(default='allow', rules=deny_rules(SECRETS_PATTERNS, SECRETS_DESCRIPTION)), write=OperationPermissions(default='allow', rules=deny_rules(SECRETS_PATTERNS + SYSTEM_PATTERNS, SYSTEM_DESCRIPTION)), edit=OperationPermissions(default='allow', rules=deny_rules(SECRETS_PATTERNS + SYSTEM_PATTERNS, SYSTEM_DESCRIPTION)), execute=OperationPermissions(default='allow', rules=deny_rules(DANGEROUS_COMMANDS, DANGEROUS_DESCRIPTION)), glob=OperationPermissions(default='allow'), grep=OperationPermissions(default='allow'), ls=OperationPermissions(default='allow')) module-attribute

Everything allowed except secrets, system paths and dangerous commands.

READONLY_RULESET

pydantic_ai_backends.permissions.presets.READONLY_RULESET = PermissionRuleset(default='deny', read=OperationPermissions(default='allow', rules=deny_rules(SECRETS_PATTERNS, SECRETS_DESCRIPTION)), write=OperationPermissions(default='deny'), edit=OperationPermissions(default='deny'), execute=OperationPermissions(default='deny'), glob=OperationPermissions(default='allow'), grep=OperationPermissions(default='allow'), ls=OperationPermissions(default='allow')) module-attribute

Reads, listings and searches only — nothing may change or run.

STRICT_RULESET

pydantic_ai_backends.permissions.presets.STRICT_RULESET = PermissionRuleset(default='ask', read=OperationPermissions(default='ask', rules=deny_rules(SECRETS_PATTERNS, SECRETS_DESCRIPTION)), write=OperationPermissions(default='ask', rules=deny_rules(SECRETS_PATTERNS, SECRETS_DESCRIPTION)), edit=OperationPermissions(default='ask', rules=deny_rules(SECRETS_PATTERNS, SECRETS_DESCRIPTION)), execute=OperationPermissions(default='ask', rules=deny_rules(DANGEROUS_COMMANDS, DANGEROUS_DESCRIPTION)), glob=OperationPermissions(default='ask'), grep=OperationPermissions(default='ask'), ls=OperationPermissions(default='ask')) module-attribute

Every operation requires explicit approval.

create_ruleset

pydantic_ai_backends.permissions.presets.create_ruleset(*, default='ask', allow_read=True, allow_write=False, allow_edit=False, allow_execute=False, allow_glob=True, allow_grep=True, allow_ls=True, deny_secrets=True)

Build a ruleset from per-operation allow/ask switches.

Each allow_* flag chooses between "allow" and "ask" for that operation's default.

Parameters:

Name Type Description Default
default PermissionAction

Global default for operations with no configuration.

'ask'
allow_read bool

Allow reads outright rather than asking.

True
allow_write bool

Allow writes outright rather than asking.

False
allow_edit bool

Allow edits outright rather than asking.

False
allow_execute bool

Allow commands outright rather than asking.

False
allow_glob bool

Allow globbing outright rather than asking.

True
allow_grep bool

Allow searching outright rather than asking.

True
allow_ls bool

Allow listings outright rather than asking.

True
deny_secrets bool

Deny the paths in SECRETS_PATTERNS for read/write/edit.

True
Example
Python
ruleset = create_ruleset(allow_read=True, allow_write=True, allow_execute=False)
Source code in src/pydantic_ai_backends/permissions/presets.py
Python
def create_ruleset(
    *,
    default: PermissionAction = "ask",
    allow_read: bool = True,
    allow_write: bool = False,
    allow_edit: bool = False,
    allow_execute: bool = False,
    allow_glob: bool = True,
    allow_grep: bool = True,
    allow_ls: bool = True,
    deny_secrets: bool = True,
) -> PermissionRuleset:
    """Build a ruleset from per-operation allow/ask switches.

    Each `allow_*` flag chooses between `"allow"` and `"ask"` for that
    operation's default.

    Args:
        default: Global default for operations with no configuration.
        allow_read: Allow reads outright rather than asking.
        allow_write: Allow writes outright rather than asking.
        allow_edit: Allow edits outright rather than asking.
        allow_execute: Allow commands outright rather than asking.
        allow_glob: Allow globbing outright rather than asking.
        allow_grep: Allow searching outright rather than asking.
        allow_ls: Allow listings outright rather than asking.
        deny_secrets: Deny the paths in `SECRETS_PATTERNS` for read/write/edit.

    Example:
        ```python
        ruleset = create_ruleset(allow_read=True, allow_write=True, allow_execute=False)
        ```
    """
    secret_rules = deny_rules(SECRETS_PATTERNS, SECRETS_DESCRIPTION) if deny_secrets else []

    return PermissionRuleset(
        default=default,
        read=OperationPermissions(default=_allow_or_ask(allow_read), rules=secret_rules),
        write=OperationPermissions(default=_allow_or_ask(allow_write), rules=secret_rules),
        edit=OperationPermissions(default=_allow_or_ask(allow_edit), rules=secret_rules),
        execute=OperationPermissions(default=_allow_or_ask(allow_execute)),
        glob=OperationPermissions(default=_allow_or_ask(allow_glob)),
        grep=OperationPermissions(default=_allow_or_ask(allow_grep)),
        ls=OperationPermissions(default=_allow_or_ask(allow_ls)),
    )

Patterns

SECRETS_PATTERNS

pydantic_ai_backends.permissions.presets.SECRETS_PATTERNS = ['**/.env', '**/.env.*', '**/*.pem', '**/*.key', '**/*.crt', '**/credentials*', '**/secrets*', '**/*secret*', '**/*password*', '**/.aws/**', '**/.ssh/**', '**/.gnupg/**'] module-attribute

Paths that typically hold credentials.

SYSTEM_PATTERNS

pydantic_ai_backends.permissions.presets.SYSTEM_PATTERNS = ['/etc/**', '/var/**', '/usr/**', '/bin/**', '/sbin/**', '/boot/**', '/sys/**', '/proc/**'] module-attribute

Paths owned by the operating system rather than the workspace.

Callback Types

AskCallback

pydantic_ai_backends.permissions.checker.AskCallback = Callable[[PermissionOperation, str, str], Awaitable[bool]] module-attribute

Approval callback: (operation, target, reason) -> whether to allow.

AskFallback

pydantic_ai_backends.permissions.checker.AskFallback = Literal['deny', 'error'] module-attribute

What an "ask" does when no callback can answer it.